From: Matthias Clasen Date: Fri, 7 Oct 2022 21:47:28 +0000 (-0400) Subject: Deprecate treeviews and cell renderers X-Git-Tag: archive/raspbian/4.12.3+ds-1+rpi1~1^2^2^2~22^2~9^2~182^2~4 X-Git-Url: https://dgit.raspbian.org/%22http://www.example.com/cgi/success//%22http:/www.example.com/cgi/success/?a=commitdiff_plain;h=5e256590db9124d2a71be3fe41e052a56ab2c46d;p=gtk4.git Deprecate treeviews and cell renderers This includes GtkCellArea GtkCellAreaBox GtkCellAreaContext GtkCellEditable GtkCellRenderer GtkCellRendererAccel GtkCellRendererCombo GtkCellRendererPixbuf GtkCellRendererProgress GtkCellRendererSpin GtkCellRendererSpinner GtkCellRendererText GtkCellRendererToggle GtkCellView GtkComboBox GtkComboBoxText GtkIconView GtkListStore GtkTreeModel GtkTreeModelFilter GtkTreeModelSort GtkTreeStore GtkTreeView GtkTreeViewColumn GtkTreeSelection --- diff --git a/demos/gtk-demo/filtermodel.c b/demos/gtk-demo/filtermodel.c index b4f07337b1..78056eecc7 100644 --- a/demos/gtk-demo/filtermodel.c +++ b/demos/gtk-demo/filtermodel.c @@ -9,6 +9,8 @@ #include #include +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + enum { WIDTH_COLUMN, HEIGHT_COLUMN, diff --git a/demos/gtk-demo/list_store.c b/demos/gtk-demo/list_store.c index 7619a51a68..e71e78e4a3 100644 --- a/demos/gtk-demo/list_store.c +++ b/demos/gtk-demo/list_store.c @@ -7,6 +7,8 @@ #include +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + static GtkWidget *window = NULL; static GtkTreeModel *model = NULL; static guint timeout = 0; diff --git a/gtk/a11y/gtkatspiselection.c b/gtk/a11y/gtkatspiselection.c index 9ac86b7b11..e5d9172f14 100644 --- a/gtk/a11y/gtkatspiselection.c +++ b/gtk/a11y/gtkatspiselection.c @@ -31,7 +31,7 @@ #include "gtklistbase.h" #include "gtklistbox.h" #include "gtkflowbox.h" -#include "gtkcombobox.h" +#include "deprecated/gtkcombobox.h" #include "gtkstackswitcher.h" #include "gtknotebook.h" #include "gtklistview.h" @@ -618,6 +618,8 @@ static const GDBusInterfaceVTable flowbox_vtable = { /* }}} */ /* {{{ GtkComboBox */ +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + static void combobox_handle_method (GDBusConnection *connection, const gchar *sender, @@ -713,6 +715,8 @@ static const GDBusInterfaceVTable combobox_vtable = { NULL }; +G_GNUC_END_IGNORE_DEPRECATIONS + /* }}} */ /* {{{ GtkStackSwitcher */ diff --git a/gtk/deprecated/gtkcellarea.c b/gtk/deprecated/gtkcellarea.c new file mode 100644 index 0000000000..7f8d9c88b4 --- /dev/null +++ b/gtk/deprecated/gtkcellarea.c @@ -0,0 +1,3641 @@ +/* gtkcellarea.c + * + * Copyright (C) 2010 Openismus GmbH + * + * Authors: + * Tristan Van Berkom + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +/** + * GtkCellArea: + * + * An abstract class for laying out `GtkCellRenderer`s + * + * The `GtkCellArea` is an abstract class for [iface@Gtk.CellLayout] + * widgets (also referred to as "layouting widgets") to interface with + * an arbitrary number of [class@Gtk.CellRenderer]s and interact with the user + * for a given [iface@Gtk.TreeModel] row. + * + * The cell area handles events, focus navigation, drawing and + * size requests and allocations for a given row of data. + * + * Usually users dont have to interact with the `GtkCellArea` directly + * unless they are implementing a cell-layouting widget themselves. + * + * # Requesting area sizes + * + * As outlined in + * [GtkWidget’s geometry management section](class.Widget.html#height-for-width-geometry-management), + * GTK uses a height-for-width + * geometry management system to compute the sizes of widgets and user + * interfaces. `GtkCellArea` uses the same semantics to calculate the + * size of an area for an arbitrary number of `GtkTreeModel` rows. + * + * When requesting the size of a cell area one needs to calculate + * the size for a handful of rows, and this will be done differently by + * different layouting widgets. For instance a [class@Gtk.TreeViewColumn] + * always lines up the areas from top to bottom while a [class@Gtk.IconView] + * on the other hand might enforce that all areas received the same + * width and wrap the areas around, requesting height for more cell + * areas when allocated less width. + * + * It’s also important for areas to maintain some cell + * alignments with areas rendered for adjacent rows (cells can + * appear “columnized” inside an area even when the size of + * cells are different in each row). For this reason the `GtkCellArea` + * uses a [class@Gtk.CellAreaContext] object to store the alignments + * and sizes along the way (as well as the overall largest minimum + * and natural size for all the rows which have been calculated + * with the said context). + * + * The [class@Gtk.CellAreaContext] is an opaque object specific to the + * `GtkCellArea` which created it (see [method@Gtk.CellArea.create_context]). + * + * The owning cell-layouting widget can create as many contexts as + * it wishes to calculate sizes of rows which should receive the + * same size in at least one orientation (horizontally or vertically), + * However, it’s important that the same [class@Gtk.CellAreaContext] which + * was used to request the sizes for a given `GtkTreeModel` row be + * used when rendering or processing events for that row. + * + * In order to request the width of all the rows at the root level + * of a `GtkTreeModel` one would do the following: + * + * ```c + * GtkTreeIter iter; + * int minimum_width; + * int natural_width; + * + * valid = gtk_tree_model_get_iter_first (model, &iter); + * while (valid) + * { + * gtk_cell_area_apply_attributes (area, model, &iter, FALSE, FALSE); + * gtk_cell_area_get_preferred_width (area, context, widget, NULL, NULL); + * + * valid = gtk_tree_model_iter_next (model, &iter); + * } + * + * gtk_cell_area_context_get_preferred_width (context, &minimum_width, &natural_width); + * ``` + * + * Note that in this example it’s not important to observe the + * returned minimum and natural width of the area for each row + * unless the cell-layouting object is actually interested in the + * widths of individual rows. The overall width is however stored + * in the accompanying `GtkCellAreaContext` object and can be consulted + * at any time. + * + * This can be useful since `GtkCellLayout` widgets usually have to + * support requesting and rendering rows in treemodels with an + * exceedingly large amount of rows. The `GtkCellLayout` widget in + * that case would calculate the required width of the rows in an + * idle or timeout source (see [func@GLib.timeout_add]) and when the widget + * is requested its actual width in [vfunc@Gtk.Widget.measure] + * it can simply consult the width accumulated so far in the + * `GtkCellAreaContext` object. + * + * A simple example where rows are rendered from top to bottom and + * take up the full width of the layouting widget would look like: + * + * ```c + * static void + * foo_get_preferred_width (GtkWidget *widget, + * int *minimum_size, + * int *natural_size) + * { + * Foo *self = FOO (widget); + * FooPrivate *priv = foo_get_instance_private (self); + * + * foo_ensure_at_least_one_handfull_of_rows_have_been_requested (self); + * + * gtk_cell_area_context_get_preferred_width (priv->context, minimum_size, natural_size); + * } + * ``` + * + * In the above example the `Foo` widget has to make sure that some + * row sizes have been calculated (the amount of rows that `Foo` judged + * was appropriate to request space for in a single timeout iteration) + * before simply returning the amount of space required by the area via + * the `GtkCellAreaContext`. + * + * Requesting the height for width (or width for height) of an area is + * a similar task except in this case the `GtkCellAreaContext` does not + * store the data (actually, it does not know how much space the layouting + * widget plans to allocate it for every row. It’s up to the layouting + * widget to render each row of data with the appropriate height and + * width which was requested by the `GtkCellArea`). + * + * In order to request the height for width of all the rows at the + * root level of a `GtkTreeModel` one would do the following: + * + * ```c + * GtkTreeIter iter; + * int minimum_height; + * int natural_height; + * int full_minimum_height = 0; + * int full_natural_height = 0; + * + * valid = gtk_tree_model_get_iter_first (model, &iter); + * while (valid) + * { + * gtk_cell_area_apply_attributes (area, model, &iter, FALSE, FALSE); + * gtk_cell_area_get_preferred_height_for_width (area, context, widget, + * width, &minimum_height, &natural_height); + * + * if (width_is_for_allocation) + * cache_row_height (&iter, minimum_height, natural_height); + * + * full_minimum_height += minimum_height; + * full_natural_height += natural_height; + * + * valid = gtk_tree_model_iter_next (model, &iter); + * } + * ``` + * + * Note that in the above example we would need to cache the heights + * returned for each row so that we would know what sizes to render the + * areas for each row. However we would only want to really cache the + * heights if the request is intended for the layouting widgets real + * allocation. + * + * In some cases the layouting widget is requested the height for an + * arbitrary for_width, this is a special case for layouting widgets + * who need to request size for tens of thousands of rows. For this + * case it’s only important that the layouting widget calculate + * one reasonably sized chunk of rows and return that height + * synchronously. The reasoning here is that any layouting widget is + * at least capable of synchronously calculating enough height to fill + * the screen height (or scrolled window height) in response to a single + * call to [vfunc@Gtk.Widget.measure]. Returning + * a perfect height for width that is larger than the screen area is + * inconsequential since after the layouting receives an allocation + * from a scrolled window it simply continues to drive the scrollbar + * values while more and more height is required for the row heights + * that are calculated in the background. + * + * # Rendering Areas + * + * Once area sizes have been acquired at least for the rows in the + * visible area of the layouting widget they can be rendered at + * [vfunc@Gtk.Widget.snapshot] time. + * + * A crude example of how to render all the rows at the root level + * runs as follows: + * + * ```c + * GtkAllocation allocation; + * GdkRectangle cell_area = { 0, }; + * GtkTreeIter iter; + * int minimum_width; + * int natural_width; + * + * gtk_widget_get_allocation (widget, &allocation); + * cell_area.width = allocation.width; + * + * valid = gtk_tree_model_get_iter_first (model, &iter); + * while (valid) + * { + * cell_area.height = get_cached_height_for_row (&iter); + * + * gtk_cell_area_apply_attributes (area, model, &iter, FALSE, FALSE); + * gtk_cell_area_render (area, context, widget, cr, + * &cell_area, &cell_area, state_flags, FALSE); + * + * cell_area.y += cell_area.height; + * + * valid = gtk_tree_model_iter_next (model, &iter); + * } + * ``` + * + * Note that the cached height in this example really depends on how + * the layouting widget works. The layouting widget might decide to + * give every row its minimum or natural height or, if the model content + * is expected to fit inside the layouting widget without scrolling, it + * would make sense to calculate the allocation for each row at + * the time the widget is allocated using [func@Gtk.distribute_natural_allocation]. + * + * # Handling Events and Driving Keyboard Focus + * + * Passing events to the area is as simple as handling events on any + * normal widget and then passing them to the [method@Gtk.CellArea.event] + * API as they come in. Usually `GtkCellArea` is only interested in + * button events, however some customized derived areas can be implemented + * who are interested in handling other events. Handling an event can + * trigger the [`signal@Gtk.CellArea::focus-changed`] signal to fire; as well + * as [`signal@GtkCellArea::add-editable`] in the case that an editable cell + * was clicked and needs to start editing. You can call + * [method@Gtk.CellArea.stop_editing] at any time to cancel any cell editing + * that is currently in progress. + * + * The `GtkCellArea` drives keyboard focus from cell to cell in a way + * similar to `GtkWidget`. For layouting widgets that support giving + * focus to cells it’s important to remember to pass `GTK_CELL_RENDERER_FOCUSED` + * to the area functions for the row that has focus and to tell the + * area to paint the focus at render time. + * + * Layouting widgets that accept focus on cells should implement the + * [vfunc@Gtk.Widget.focus] virtual method. The layouting widget is always + * responsible for knowing where `GtkTreeModel` rows are rendered inside + * the widget, so at [vfunc@Gtk.Widget.focus] time the layouting widget + * should use the `GtkCellArea` methods to navigate focus inside the area + * and then observe the [enum@Gtk.DirectionType] to pass the focus to adjacent + * rows and areas. + * + * A basic example of how the [vfunc@Gtk.Widget.focus] virtual method + * should be implemented: + * + * ``` + * static gboolean + * foo_focus (GtkWidget *widget, + * GtkDirectionType direction) + * { + * Foo *self = FOO (widget); + * FooPrivate *priv = foo_get_instance_private (self); + * int focus_row = priv->focus_row; + * gboolean have_focus = FALSE; + * + * if (!gtk_widget_has_focus (widget)) + * gtk_widget_grab_focus (widget); + * + * valid = gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, priv->focus_row); + * while (valid) + * { + * gtk_cell_area_apply_attributes (priv->area, priv->model, &iter, FALSE, FALSE); + * + * if (gtk_cell_area_focus (priv->area, direction)) + * { + * priv->focus_row = focus_row; + * have_focus = TRUE; + * break; + * } + * else + * { + * if (direction == GTK_DIR_RIGHT || + * direction == GTK_DIR_LEFT) + * break; + * else if (direction == GTK_DIR_UP || + * direction == GTK_DIR_TAB_BACKWARD) + * { + * if (focus_row == 0) + * break; + * else + * { + * focus_row--; + * valid = gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, focus_row); + * } + * } + * else + * { + * if (focus_row == last_row) + * break; + * else + * { + * focus_row++; + * valid = gtk_tree_model_iter_next (priv->model, &iter); + * } + * } + * } + * } + * return have_focus; + * } + * ``` + * + * Note that the layouting widget is responsible for matching the + * `GtkDirectionType` values to the way it lays out its cells. + * + * # Cell Properties + * + * The `GtkCellArea` introduces cell properties for `GtkCellRenderer`s. + * This provides some general interfaces for defining the relationship + * cell areas have with their cells. For instance in a [class@Gtk.CellAreaBox] + * a cell might “expand” and receive extra space when the area is allocated + * more than its full natural request, or a cell might be configured to “align” + * with adjacent rows which were requested and rendered with the same + * `GtkCellAreaContext`. + * + * Use [method@Gtk.CellAreaClass.install_cell_property] to install cell + * properties for a cell area class and [method@Gtk.CellAreaClass.find_cell_property] + * or [method@Gtk.CellAreaClass.list_cell_properties] to get information about + * existing cell properties. + * + * To set the value of a cell property, use [method@Gtk.CellArea.cell_set_property], + * [method@Gtk.CellArea.cell_set] or [method@Gtk.CellArea.cell_set_valist]. To obtain + * the value of a cell property, use [method@Gtk.CellArea.cell_get_property] + * [method@Gtk.CellArea.cell_get] or [method@Gtk.CellArea.cell_get_valist]. + */ + +#include "config.h" + +#include +#include +#include + +#include "deprecated/gtkcelllayout.h" +#include "gtkcellarea.h" +#include "deprecated/gtkcellareacontext.h" +#include "gtkmarshalers.h" +#include "gtkprivate.h" +#include "gtksnapshot.h" +#include "gtkstylecontext.h" +#include "gtknative.h" + +#include + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + +/* GObjectClass */ +static void gtk_cell_area_dispose (GObject *object); +static void gtk_cell_area_finalize (GObject *object); +static void gtk_cell_area_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gtk_cell_area_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); + +/* GtkCellAreaClass */ +static void gtk_cell_area_real_add (GtkCellArea *area, + GtkCellRenderer *renderer); +static void gtk_cell_area_real_remove (GtkCellArea *area, + GtkCellRenderer *renderer); +static void gtk_cell_area_real_foreach (GtkCellArea *area, + GtkCellCallback callback, + gpointer callback_data); +static void gtk_cell_area_real_foreach_alloc (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + const GdkRectangle *cell_area, + const GdkRectangle *background_area, + GtkCellAllocCallback callback, + gpointer callback_data); +static int gtk_cell_area_real_event (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + GdkEvent *event, + const GdkRectangle *cell_area, + GtkCellRendererState flags); +static void gtk_cell_area_real_snapshot (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + GtkSnapshot *snapshot, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + GtkCellRendererState flags, + gboolean paint_focus); +static void gtk_cell_area_real_apply_attributes (GtkCellArea *area, + GtkTreeModel *tree_model, + GtkTreeIter *iter, + gboolean is_expander, + gboolean is_expanded); + +static GtkCellAreaContext *gtk_cell_area_real_create_context (GtkCellArea *area); +static GtkCellAreaContext *gtk_cell_area_real_copy_context (GtkCellArea *area, + GtkCellAreaContext *context); +static GtkSizeRequestMode gtk_cell_area_real_get_request_mode (GtkCellArea *area); +static void gtk_cell_area_real_get_preferred_width (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + int *minimum_width, + int *natural_width); +static void gtk_cell_area_real_get_preferred_height (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + int *minimum_height, + int *natural_height); +static void gtk_cell_area_real_get_preferred_height_for_width (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + int width, + int *minimum_height, + int *natural_height); +static void gtk_cell_area_real_get_preferred_width_for_height (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + int height, + int *minimum_width, + int *natural_width); +static gboolean gtk_cell_area_real_is_activatable (GtkCellArea *area); +static gboolean gtk_cell_area_real_activate (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + const GdkRectangle *cell_area, + GtkCellRendererState flags, + gboolean edit_only); +static gboolean gtk_cell_area_real_focus (GtkCellArea *area, + GtkDirectionType direction); + +/* GtkCellLayoutIface */ +static void gtk_cell_area_cell_layout_init (GtkCellLayoutIface *iface); +static void gtk_cell_area_pack_default (GtkCellLayout *cell_layout, + GtkCellRenderer *renderer, + gboolean expand); +static void gtk_cell_area_clear (GtkCellLayout *cell_layout); +static void gtk_cell_area_add_attribute (GtkCellLayout *cell_layout, + GtkCellRenderer *renderer, + const char *attribute, + int column); +static void gtk_cell_area_set_cell_data_func (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + GtkCellLayoutDataFunc func, + gpointer func_data, + GDestroyNotify destroy); +static void gtk_cell_area_clear_attributes (GtkCellLayout *cell_layout, + GtkCellRenderer *renderer); +static void gtk_cell_area_reorder (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + int position); +static GList *gtk_cell_area_get_cells (GtkCellLayout *cell_layout); +static GtkCellArea *gtk_cell_area_get_area (GtkCellLayout *cell_layout); + +/* GtkBuildableIface */ +static void gtk_cell_area_buildable_init (GtkBuildableIface *iface); +static void gtk_cell_area_buildable_custom_tag_end (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const char *tagname, + gpointer data); + +/* Used in foreach loop to check if a child renderer is present */ +typedef struct { + GtkCellRenderer *renderer; + gboolean has_renderer; +} HasRendererCheck; + +/* Used in foreach loop to get a cell's allocation */ +typedef struct { + GtkCellRenderer *renderer; + GdkRectangle allocation; +} RendererAllocationData; + +/* Used in foreach loop to render cells */ +typedef struct { + GtkCellArea *area; + GtkWidget *widget; + GtkSnapshot *snapshot; + GdkRectangle focus_rect; + GtkCellRendererState render_flags; + guint paint_focus : 1; + guint focus_all : 1; + guint first_focus : 1; +} CellRenderData; + +/* Used in foreach loop to get a cell by position */ +typedef struct { + int x; + int y; + GtkCellRenderer *renderer; + GdkRectangle cell_area; +} CellByPositionData; + +/* Attribute/Cell metadata */ +typedef struct { + const char *attribute; + int column; +} CellAttribute; + +typedef struct { + GSList *attributes; + + GtkCellLayoutDataFunc func; + gpointer data; + GDestroyNotify destroy; + GtkCellLayout *proxy; +} CellInfo; + +static CellInfo *cell_info_new (GtkCellLayoutDataFunc func, + gpointer data, + GDestroyNotify destroy); +static void cell_info_free (CellInfo *info); +static CellAttribute *cell_attribute_new (GtkCellRenderer *renderer, + const char *attribute, + int column); +static void cell_attribute_free (CellAttribute *attribute); +static int cell_attribute_find (CellAttribute *cell_attribute, + const char *attribute); + +/* Internal functions/signal emissions */ +static void gtk_cell_area_add_editable (GtkCellArea *area, + GtkCellRenderer *renderer, + GtkCellEditable *editable, + const GdkRectangle *cell_area); +static void gtk_cell_area_remove_editable (GtkCellArea *area, + GtkCellRenderer *renderer, + GtkCellEditable *editable); +static void gtk_cell_area_set_edit_widget (GtkCellArea *area, + GtkCellEditable *editable); +static void gtk_cell_area_set_edited_cell (GtkCellArea *area, + GtkCellRenderer *renderer); + + +/* Struct to pass data along while looping over + * cell renderers to apply attributes + */ +typedef struct { + GtkCellArea *area; + GtkTreeModel *model; + GtkTreeIter *iter; + gboolean is_expander; + gboolean is_expanded; +} AttributeData; + +typedef struct _GtkCellAreaPrivate GtkCellAreaPrivate; + +struct _GtkCellAreaPrivate +{ + /* The GtkCellArea bookkeeps any connected + * attributes in this hash table. + */ + GHashTable *cell_info; + + /* Current path is saved as a side-effect + * of gtk_cell_area_apply_attributes() + */ + char *current_path; + + /* Current cell being edited and editable widget used */ + GtkCellEditable *edit_widget; + GtkCellRenderer *edited_cell; + + /* Signal connections to the editable widget */ + gulong remove_widget_id; + + /* Currently focused cell */ + GtkCellRenderer *focus_cell; + + /* Tracking which cells are focus siblings of focusable cells */ + GHashTable *focus_siblings; +}; + +enum { + PROP_0, + PROP_FOCUS_CELL, + PROP_EDITED_CELL, + PROP_EDIT_WIDGET +}; + +enum { + SIGNAL_APPLY_ATTRIBUTES, + SIGNAL_ADD_EDITABLE, + SIGNAL_REMOVE_EDITABLE, + SIGNAL_FOCUS_CHANGED, + LAST_SIGNAL +}; + +/* Keep the paramspec pool internal, no need to deliver notifications + * on cells. at least no perceived need for now + */ +static GParamSpecPool *cell_property_pool = NULL; +static guint cell_area_signals[LAST_SIGNAL] = { 0 }; + +#define PARAM_SPEC_PARAM_ID(pspec) ((pspec)->param_id) +#define PARAM_SPEC_SET_PARAM_ID(pspec, id) ((pspec)->param_id = (id)) + +G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GtkCellArea, gtk_cell_area, G_TYPE_INITIALLY_UNOWNED, + G_ADD_PRIVATE (GtkCellArea) + G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT, + gtk_cell_area_cell_layout_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, + gtk_cell_area_buildable_init)) + +static void +gtk_cell_area_init (GtkCellArea *area) +{ + GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); + + priv->cell_info = g_hash_table_new_full (g_direct_hash, + g_direct_equal, + NULL, + (GDestroyNotify)cell_info_free); + + priv->focus_siblings = g_hash_table_new_full (g_direct_hash, + g_direct_equal, + NULL, + (GDestroyNotify)g_list_free); + + priv->focus_cell = NULL; + priv->edited_cell = NULL; + priv->edit_widget = NULL; + + priv->remove_widget_id = 0; +} + +static void +gtk_cell_area_class_init (GtkCellAreaClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + /* GObjectClass */ + object_class->dispose = gtk_cell_area_dispose; + object_class->finalize = gtk_cell_area_finalize; + object_class->get_property = gtk_cell_area_get_property; + object_class->set_property = gtk_cell_area_set_property; + + /* general */ + class->add = gtk_cell_area_real_add; + class->remove = gtk_cell_area_real_remove; + class->foreach = gtk_cell_area_real_foreach; + class->foreach_alloc = gtk_cell_area_real_foreach_alloc; + class->event = gtk_cell_area_real_event; + class->snapshot = gtk_cell_area_real_snapshot; + class->apply_attributes = gtk_cell_area_real_apply_attributes; + + /* geometry */ + class->create_context = gtk_cell_area_real_create_context; + class->copy_context = gtk_cell_area_real_copy_context; + class->get_request_mode = gtk_cell_area_real_get_request_mode; + class->get_preferred_width = gtk_cell_area_real_get_preferred_width; + class->get_preferred_height = gtk_cell_area_real_get_preferred_height; + class->get_preferred_height_for_width = gtk_cell_area_real_get_preferred_height_for_width; + class->get_preferred_width_for_height = gtk_cell_area_real_get_preferred_width_for_height; + + /* focus */ + class->is_activatable = gtk_cell_area_real_is_activatable; + class->activate = gtk_cell_area_real_activate; + class->focus = gtk_cell_area_real_focus; + + /* Signals */ + /** + * GtkCellArea::apply-attributes: + * @area: the `GtkCellArea` to apply the attributes to + * @model: the `GtkTreeModel` to apply the attributes from + * @iter: the `GtkTreeIter` indicating which row to apply the attributes of + * @is_expander: whether the view shows children for this row + * @is_expanded: whether the view is currently showing the children of this row + * + * This signal is emitted whenever applying attributes to @area from @model + */ + cell_area_signals[SIGNAL_APPLY_ATTRIBUTES] = + g_signal_new (I_("apply-attributes"), + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GtkCellAreaClass, apply_attributes), + NULL, NULL, + _gtk_marshal_VOID__OBJECT_BOXED_BOOLEAN_BOOLEAN, + G_TYPE_NONE, 4, + GTK_TYPE_TREE_MODEL, + GTK_TYPE_TREE_ITER, + G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN); + g_signal_set_va_marshaller (cell_area_signals[SIGNAL_APPLY_ATTRIBUTES], G_TYPE_FROM_CLASS (class), + _gtk_marshal_VOID__OBJECT_BOXED_BOOLEAN_BOOLEANv); + + /** + * GtkCellArea::add-editable: + * @area: the `GtkCellArea` where editing started + * @renderer: the `GtkCellRenderer` that started the edited + * @editable: the `GtkCellEditable` widget to add + * @cell_area: the `GtkWidget` relative `GdkRectangle` coordinates + * where @editable should be added + * @path: the `GtkTreePath` string this edit was initiated for + * + * Indicates that editing has started on @renderer and that @editable + * should be added to the owning cell-layouting widget at @cell_area. + */ + cell_area_signals[SIGNAL_ADD_EDITABLE] = + g_signal_new (I_("add-editable"), + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, /* No class closure here */ + NULL, NULL, + _gtk_marshal_VOID__OBJECT_OBJECT_BOXED_STRING, + G_TYPE_NONE, 4, + GTK_TYPE_CELL_RENDERER, + GTK_TYPE_CELL_EDITABLE, + GDK_TYPE_RECTANGLE, + G_TYPE_STRING); + + + /** + * GtkCellArea::remove-editable: + * @area: the `GtkCellArea` where editing finished + * @renderer: the `GtkCellRenderer` that finished editeding + * @editable: the `GtkCellEditable` widget to remove + * + * Indicates that editing finished on @renderer and that @editable + * should be removed from the owning cell-layouting widget. + */ + cell_area_signals[SIGNAL_REMOVE_EDITABLE] = + g_signal_new (I_("remove-editable"), + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, /* No class closure here */ + NULL, NULL, + _gtk_marshal_VOID__OBJECT_OBJECT, + G_TYPE_NONE, 2, + GTK_TYPE_CELL_RENDERER, + GTK_TYPE_CELL_EDITABLE); + + /** + * GtkCellArea::focus-changed: + * @area: the `GtkCellArea` where focus changed + * @renderer: the `GtkCellRenderer` that has focus + * @path: the current `GtkTreePath` string set for @area + * + * Indicates that focus changed on this @area. This signal + * is emitted either as a result of focus handling or event + * handling. + * + * It's possible that the signal is emitted even if the + * currently focused renderer did not change, this is + * because focus may change to the same renderer in the + * same cell area for a different row of data. + */ + cell_area_signals[SIGNAL_FOCUS_CHANGED] = + g_signal_new (I_("focus-changed"), + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, /* No class closure here */ + NULL, NULL, + _gtk_marshal_VOID__OBJECT_STRING, + G_TYPE_NONE, 2, + GTK_TYPE_CELL_RENDERER, + G_TYPE_STRING); + + /* Properties */ + /** + * GtkCellArea:focus-cell: + * + * The cell in the area that currently has focus + */ + g_object_class_install_property (object_class, + PROP_FOCUS_CELL, + g_param_spec_object ("focus-cell", NULL, NULL, + GTK_TYPE_CELL_RENDERER, + GTK_PARAM_READWRITE)); + + /** + * GtkCellArea:edited-cell: + * + * The cell in the area that is currently edited + * + * This property is read-only and only changes as + * a result of a call gtk_cell_area_activate_cell(). + */ + g_object_class_install_property (object_class, + PROP_EDITED_CELL, + g_param_spec_object ("edited-cell", NULL, NULL, + GTK_TYPE_CELL_RENDERER, + GTK_PARAM_READABLE)); + + /** + * GtkCellArea:edit-widget: + * + * The widget currently editing the edited cell + * + * This property is read-only and only changes as + * a result of a call gtk_cell_area_activate_cell(). + */ + g_object_class_install_property (object_class, + PROP_EDIT_WIDGET, + g_param_spec_object ("edit-widget", NULL, NULL, + GTK_TYPE_CELL_EDITABLE, + GTK_PARAM_READABLE)); + + /* Pool for Cell Properties */ + if (!cell_property_pool) + cell_property_pool = g_param_spec_pool_new (FALSE); +} + +/************************************************************* + * CellInfo Basics * + *************************************************************/ +static CellInfo * +cell_info_new (GtkCellLayoutDataFunc func, + gpointer data, + GDestroyNotify destroy) +{ + CellInfo *info = g_slice_new0 (CellInfo); + + info->func = func; + info->data = data; + info->destroy = destroy; + + return info; +} + +static void +cell_info_free (CellInfo *info) +{ + if (info->destroy) + info->destroy (info->data); + + g_slist_free_full (info->attributes, (GDestroyNotify)cell_attribute_free); + + g_slice_free (CellInfo, info); +} + +static CellAttribute * +cell_attribute_new (GtkCellRenderer *renderer, + const char *attribute, + int column) +{ + GParamSpec *pspec; + + /* Check if the attribute really exists and point to + * the property string installed on the cell renderer + * class (dont dup the string) + */ + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (renderer), attribute); + + if (pspec) + { + CellAttribute *cell_attribute = g_slice_new (CellAttribute); + + cell_attribute->attribute = pspec->name; + cell_attribute->column = column; + + return cell_attribute; + } + + return NULL; +} + +static void +cell_attribute_free (CellAttribute *attribute) +{ + g_slice_free (CellAttribute, attribute); +} + +/* GCompareFunc for g_slist_find_custom() */ +static int +cell_attribute_find (CellAttribute *cell_attribute, + const char *attribute) +{ + return g_strcmp0 (cell_attribute->attribute, attribute); +} + +/************************************************************* + * GObjectClass * + *************************************************************/ +static void +gtk_cell_area_finalize (GObject *object) +{ + GtkCellArea *area = GTK_CELL_AREA (object); + GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); + + /* All cell renderers should already be removed at this point, + * just kill our (empty) hash tables here. + */ + g_hash_table_destroy (priv->cell_info); + g_hash_table_destroy (priv->focus_siblings); + + g_free (priv->current_path); + + G_OBJECT_CLASS (gtk_cell_area_parent_class)->finalize (object); +} + + +static void +gtk_cell_area_dispose (GObject *object) +{ + /* This removes every cell renderer that may be added to the GtkCellArea, + * subclasses should be breaking references to the GtkCellRenderers + * at this point. + */ + gtk_cell_layout_clear (GTK_CELL_LAYOUT (object)); + + /* Remove any ref to a focused/edited cell */ + gtk_cell_area_set_focus_cell (GTK_CELL_AREA (object), NULL); + gtk_cell_area_set_edited_cell (GTK_CELL_AREA (object), NULL); + gtk_cell_area_set_edit_widget (GTK_CELL_AREA (object), NULL); + + G_OBJECT_CLASS (gtk_cell_area_parent_class)->dispose (object); +} + +static void +gtk_cell_area_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkCellArea *area = GTK_CELL_AREA (object); + + switch (prop_id) + { + case PROP_FOCUS_CELL: + gtk_cell_area_set_focus_cell (area, (GtkCellRenderer *)g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_cell_area_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkCellArea *area = GTK_CELL_AREA (object); + GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); + + switch (prop_id) + { + case PROP_FOCUS_CELL: + g_value_set_object (value, priv->focus_cell); + break; + case PROP_EDITED_CELL: + g_value_set_object (value, priv->edited_cell); + break; + case PROP_EDIT_WIDGET: + g_value_set_object (value, priv->edit_widget); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +/************************************************************* + * GtkCellAreaClass * + *************************************************************/ +static void +gtk_cell_area_real_add (GtkCellArea *area, + GtkCellRenderer *renderer) +{ + g_warning ("GtkCellAreaClass::add not implemented for '%s'", + g_type_name (G_TYPE_FROM_INSTANCE (area))); +} + +static void +gtk_cell_area_real_remove (GtkCellArea *area, + GtkCellRenderer *renderer) +{ + g_warning ("GtkCellAreaClass::remove not implemented for '%s'", + g_type_name (G_TYPE_FROM_INSTANCE (area))); +} + +static void +gtk_cell_area_real_foreach (GtkCellArea *area, + GtkCellCallback callback, + gpointer callback_data) +{ + g_warning ("GtkCellAreaClass::foreach not implemented for '%s'", + g_type_name (G_TYPE_FROM_INSTANCE (area))); +} + +static void +gtk_cell_area_real_foreach_alloc (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + const GdkRectangle *cell_area, + const GdkRectangle *background_area, + GtkCellAllocCallback callback, + gpointer callback_data) +{ + g_warning ("GtkCellAreaClass::foreach_alloc not implemented for '%s'", + g_type_name (G_TYPE_FROM_INSTANCE (area))); +} + +static int +gtk_cell_area_real_event (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + GdkEvent *event, + const GdkRectangle *cell_area, + GtkCellRendererState flags) +{ + GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); + gboolean retval = FALSE; + GdkEventType event_type = gdk_event_get_event_type (event); + + if (event_type == GDK_KEY_PRESS && (flags & GTK_CELL_RENDERER_FOCUSED) != 0) + { + /* Cancel any edits in progress */ + if (priv->edited_cell && + gdk_key_event_get_keyval (event) == GDK_KEY_Escape) + { + gtk_cell_area_stop_editing (area, TRUE); + retval = TRUE; + } + } + else if (event_type == GDK_BUTTON_PRESS) + { + guint button; + + button = gdk_button_event_get_button (event); + if (button == GDK_BUTTON_PRIMARY) + { + GtkCellRenderer *renderer = NULL; + GtkCellRenderer *focus_renderer; + GdkRectangle alloc_area; + double event_x, event_y; + double nx, ny; + double x, y; + GtkNative *native; + + /* We may need some semantics to tell us the offset of the event + * window we are handling events for (i.e. GtkTreeView has a bin_window) */ + gdk_event_get_position (event, &event_x, &event_y); + + native = gtk_widget_get_native (widget); + gtk_native_get_surface_transform (native, &nx, &ny); + gtk_widget_translate_coordinates (GTK_WIDGET (native), widget, event_x - nx, event_y - ny, &x, &y); + event_x = x; + event_y = y; + + /* Dont try to search for an event coordinate that is not in the area, that will + * trigger a runtime warning. + */ + if (event_x >= cell_area->x && event_x <= cell_area->x + cell_area->width && + event_y >= cell_area->y && event_y <= cell_area->y + cell_area->height) + renderer = + gtk_cell_area_get_cell_at_position (area, context, widget, + cell_area, event_x, event_y, + &alloc_area); + + if (renderer) + { + focus_renderer = gtk_cell_area_get_focus_from_sibling (area, renderer); + if (!focus_renderer) + focus_renderer = renderer; + + /* If we're already editing, cancel it and set focus */ + if (gtk_cell_area_get_edited_cell (area)) + { + /* XXX Was it really canceled in this case ? */ + gtk_cell_area_stop_editing (area, TRUE); + gtk_cell_area_set_focus_cell (area, focus_renderer); + retval = TRUE; + } + else + { + /* If we are activating via a focus sibling, + * we need to fetch the right cell area for the real event renderer */ + if (focus_renderer != renderer) + gtk_cell_area_get_cell_allocation (area, context, widget, focus_renderer, + cell_area, &alloc_area); + + gtk_cell_area_set_focus_cell (area, focus_renderer); + retval = gtk_cell_area_activate_cell (area, widget, focus_renderer, + event, &alloc_area, flags); + } + } + } + } + + return retval; +} + +static gboolean +snapshot_cell (GtkCellRenderer *renderer, + const GdkRectangle *cell_area, + const GdkRectangle *cell_background, + CellRenderData *data) +{ + GtkCellRenderer *focus_cell; + GtkCellRendererState flags; + GdkRectangle inner_area; + + focus_cell = gtk_cell_area_get_focus_cell (data->area); + flags = data->render_flags; + + gtk_cell_area_inner_cell_area (data->area, data->widget, cell_area, &inner_area); + + if ((flags & GTK_CELL_RENDERER_FOCUSED) && + (data->focus_all || + (focus_cell && + (renderer == focus_cell || + gtk_cell_area_is_focus_sibling (data->area, focus_cell, renderer))))) + { + GdkRectangle cell_focus; + + gtk_cell_renderer_get_aligned_area (renderer, data->widget, flags, &inner_area, &cell_focus); + + if (data->first_focus) + { + data->first_focus = FALSE; + data->focus_rect = cell_focus; + } + else + { + gdk_rectangle_union (&data->focus_rect, &cell_focus, &data->focus_rect); + } + } + + gtk_cell_renderer_snapshot (renderer, data->snapshot, data->widget, + cell_background, &inner_area, flags); + + return FALSE; +} + +static void +gtk_cell_area_real_snapshot (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + GtkSnapshot *snapshot, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + GtkCellRendererState flags, + gboolean paint_focus) +{ + CellRenderData render_data = + { + area, + widget, + snapshot, + { 0, }, + flags, + paint_focus, + FALSE, TRUE + }; + + /* Make sure we dont paint a focus rectangle while there + * is an editable widget in play + */ + if (gtk_cell_area_get_edited_cell (area)) + render_data.paint_focus = FALSE; + + if (!gtk_widget_has_visible_focus (widget)) + render_data.paint_focus = FALSE; + + /* If no cell can activate but the caller wants focus painted, + * then we paint focus around all cells */ + if ((flags & GTK_CELL_RENDERER_FOCUSED) != 0 && paint_focus && + !gtk_cell_area_is_activatable (area)) + render_data.focus_all = TRUE; + + gtk_cell_area_foreach_alloc (area, context, widget, cell_area, background_area, + (GtkCellAllocCallback) snapshot_cell, &render_data); + + if (render_data.paint_focus && + render_data.focus_rect.width != 0 && + render_data.focus_rect.height != 0) + { + GtkStyleContext *style_context; + GtkStateFlags renderer_state = 0; + + style_context = gtk_widget_get_style_context (widget); + gtk_style_context_save (style_context); + + renderer_state = gtk_cell_renderer_get_state (NULL, widget, flags); + gtk_style_context_set_state (style_context, renderer_state); + + gtk_snapshot_render_focus (snapshot, style_context, + render_data.focus_rect.x, render_data.focus_rect.y, + render_data.focus_rect.width, render_data.focus_rect.height); + + gtk_style_context_restore (style_context); + } +} + +static void +apply_cell_attributes (GtkCellRenderer *renderer, + CellInfo *info, + AttributeData *data) +{ + CellAttribute *attribute; + GSList *list; + GValue value = G_VALUE_INIT; + gboolean is_expander; + gboolean is_expanded; + + g_object_freeze_notify (G_OBJECT (renderer)); + + /* Whether a row expands or is presently expanded can only be + * provided by the view (as these states can vary across views + * accessing the same model). + */ + is_expander = gtk_cell_renderer_get_is_expander (renderer); + if (is_expander != data->is_expander) + gtk_cell_renderer_set_is_expander (renderer, data->is_expander); + + is_expanded = gtk_cell_renderer_get_is_expanded (renderer); + if (is_expanded != data->is_expanded) + gtk_cell_renderer_set_is_expanded (renderer, data->is_expanded); + + /* Apply the attributes directly to the renderer */ + for (list = info->attributes; list; list = list->next) + { + attribute = list->data; + + gtk_tree_model_get_value (data->model, data->iter, attribute->column, &value); + g_object_set_property (G_OBJECT (renderer), attribute->attribute, &value); + g_value_unset (&value); + } + + /* Call any GtkCellLayoutDataFunc that may have been set by the user + */ + if (info->func) + info->func (info->proxy ? info->proxy : GTK_CELL_LAYOUT (data->area), renderer, + data->model, data->iter, info->data); + + g_object_thaw_notify (G_OBJECT (renderer)); +} + +static void +gtk_cell_area_real_apply_attributes (GtkCellArea *area, + GtkTreeModel *tree_model, + GtkTreeIter *iter, + gboolean is_expander, + gboolean is_expanded) +{ + GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); + AttributeData data; + GtkTreePath *path; + + /* Feed in data needed to apply to every renderer */ + data.area = area; + data.model = tree_model; + data.iter = iter; + data.is_expander = is_expander; + data.is_expanded = is_expanded; + + /* Go over any cells that have attributes or custom GtkCellLayoutDataFuncs and + * apply the data from the treemodel */ + g_hash_table_foreach (priv->cell_info, (GHFunc)apply_cell_attributes, &data); + + /* Update the currently applied path */ + g_free (priv->current_path); + path = gtk_tree_model_get_path (tree_model, iter); + priv->current_path = gtk_tree_path_to_string (path); + gtk_tree_path_free (path); +} + +static GtkCellAreaContext * +gtk_cell_area_real_create_context (GtkCellArea *area) +{ + g_warning ("GtkCellAreaClass::create_context not implemented for '%s'", + g_type_name (G_TYPE_FROM_INSTANCE (area))); + + return NULL; +} + +static GtkCellAreaContext * +gtk_cell_area_real_copy_context (GtkCellArea *area, + GtkCellAreaContext *context) +{ + g_warning ("GtkCellAreaClass::copy_context not implemented for '%s'", + g_type_name (G_TYPE_FROM_INSTANCE (area))); + + return NULL; +} + +static GtkSizeRequestMode +gtk_cell_area_real_get_request_mode (GtkCellArea *area) +{ + /* By default cell areas are height-for-width. */ + return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH; +} + +static void +gtk_cell_area_real_get_preferred_width (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + int *minimum_width, + int *natural_width) +{ + g_warning ("GtkCellAreaClass::get_preferred_width not implemented for '%s'", + g_type_name (G_TYPE_FROM_INSTANCE (area))); +} + +static void +gtk_cell_area_real_get_preferred_height (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + int *minimum_height, + int *natural_height) +{ + g_warning ("GtkCellAreaClass::get_preferred_height not implemented for '%s'", + g_type_name (G_TYPE_FROM_INSTANCE (area))); +} + +static void +gtk_cell_area_real_get_preferred_height_for_width (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + int width, + int *minimum_height, + int *natural_height) +{ + /* If the area doesn't do height-for-width, fallback on base preferred height */ + GTK_CELL_AREA_GET_CLASS (area)->get_preferred_height (area, context, widget, minimum_height, natural_height); +} + +static void +gtk_cell_area_real_get_preferred_width_for_height (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + int height, + int *minimum_width, + int *natural_width) +{ + /* If the area doesn't do width-for-height, fallback on base preferred width */ + GTK_CELL_AREA_GET_CLASS (area)->get_preferred_width (area, context, widget, minimum_width, natural_width); +} + +static gboolean +get_is_activatable (GtkCellRenderer *renderer, + gboolean *activatable) +{ + + if (gtk_cell_renderer_is_activatable (renderer)) + *activatable = TRUE; + + return *activatable; +} + +static gboolean +gtk_cell_area_real_is_activatable (GtkCellArea *area) +{ + gboolean activatable = FALSE; + + /* Checks if any renderer can focus for the currently applied + * attributes. + * + * Subclasses can override this in the case that they are also + * rendering widgets as well as renderers. + */ + gtk_cell_area_foreach (area, (GtkCellCallback)get_is_activatable, &activatable); + + return activatable; +} + +static gboolean +gtk_cell_area_real_activate (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + const GdkRectangle *cell_area, + GtkCellRendererState flags, + gboolean edit_only) +{ + GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); + GdkRectangle renderer_area; + GtkCellRenderer *activate_cell = NULL; + GtkCellRendererMode mode; + + if (priv->focus_cell) + { + g_object_get (priv->focus_cell, "mode", &mode, NULL); + + if (gtk_cell_renderer_get_visible (priv->focus_cell) && + (edit_only ? mode == GTK_CELL_RENDERER_MODE_EDITABLE : + mode != GTK_CELL_RENDERER_MODE_INERT)) + activate_cell = priv->focus_cell; + } + else + { + GList *cells, *l; + + /* GtkTreeView sometimes wants to activate a cell when no + * cells are in focus. + */ + cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (area)); + for (l = cells; l && !activate_cell; l = l->next) + { + GtkCellRenderer *renderer = l->data; + + g_object_get (renderer, "mode", &mode, NULL); + + if (gtk_cell_renderer_get_visible (renderer) && + (edit_only ? mode == GTK_CELL_RENDERER_MODE_EDITABLE : + mode != GTK_CELL_RENDERER_MODE_INERT)) + activate_cell = renderer; + } + g_list_free (cells); + } + + if (activate_cell) + { + /* Get the allocation of the focused cell. + */ + gtk_cell_area_get_cell_allocation (area, context, widget, activate_cell, + cell_area, &renderer_area); + + /* Activate or Edit the cell + * + * Currently just not sending an event, renderers afaics dont use + * the event argument anyway, worst case is we can synthesize one. + */ + if (gtk_cell_area_activate_cell (area, widget, activate_cell, NULL, + &renderer_area, flags)) + return TRUE; + } + + return FALSE; +} + +static gboolean +gtk_cell_area_real_focus (GtkCellArea *area, + GtkDirectionType direction) +{ + g_warning ("GtkCellAreaClass::focus not implemented for '%s'", + g_type_name (G_TYPE_FROM_INSTANCE (area))); + return FALSE; +} + +/************************************************************* + * GtkCellLayoutIface * + *************************************************************/ +static void +gtk_cell_area_cell_layout_init (GtkCellLayoutIface *iface) +{ + iface->pack_start = gtk_cell_area_pack_default; + iface->pack_end = gtk_cell_area_pack_default; + iface->clear = gtk_cell_area_clear; + iface->add_attribute = gtk_cell_area_add_attribute; + iface->set_cell_data_func = gtk_cell_area_set_cell_data_func; + iface->clear_attributes = gtk_cell_area_clear_attributes; + iface->reorder = gtk_cell_area_reorder; + iface->get_cells = gtk_cell_area_get_cells; + iface->get_area = gtk_cell_area_get_area; +} + +static void +gtk_cell_area_pack_default (GtkCellLayout *cell_layout, + GtkCellRenderer *renderer, + gboolean expand) +{ + gtk_cell_area_add (GTK_CELL_AREA (cell_layout), renderer); +} + +static void +gtk_cell_area_clear (GtkCellLayout *cell_layout) +{ + GtkCellArea *area = GTK_CELL_AREA (cell_layout); + GList *l, *cells = + gtk_cell_layout_get_cells (cell_layout); + + for (l = cells; l; l = l->next) + { + GtkCellRenderer *renderer = l->data; + gtk_cell_area_remove (area, renderer); + } + + g_list_free (cells); +} + +static void +gtk_cell_area_add_attribute (GtkCellLayout *cell_layout, + GtkCellRenderer *renderer, + const char *attribute, + int column) +{ + gtk_cell_area_attribute_connect (GTK_CELL_AREA (cell_layout), + renderer, attribute, column); +} + +static void +gtk_cell_area_set_cell_data_func (GtkCellLayout *cell_layout, + GtkCellRenderer *renderer, + GtkCellLayoutDataFunc func, + gpointer func_data, + GDestroyNotify destroy) +{ + GtkCellArea *area = GTK_CELL_AREA (cell_layout); + + _gtk_cell_area_set_cell_data_func_with_proxy (area, renderer, (GFunc)func, func_data, destroy, NULL); +} + +static void +gtk_cell_area_clear_attributes (GtkCellLayout *cell_layout, + GtkCellRenderer *renderer) +{ + GtkCellArea *area = GTK_CELL_AREA (cell_layout); + GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); + CellInfo *info; + + info = g_hash_table_lookup (priv->cell_info, renderer); + + if (info) + { + g_slist_free_full (info->attributes, (GDestroyNotify)cell_attribute_free); + info->attributes = NULL; + } +} + +static void +gtk_cell_area_reorder (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + int position) +{ + g_warning ("GtkCellLayout::reorder not implemented for '%s'", + g_type_name (G_TYPE_FROM_INSTANCE (cell_layout))); +} + +static gboolean +accum_cells (GtkCellRenderer *renderer, + GList **accum) +{ + *accum = g_list_prepend (*accum, renderer); + + return FALSE; +} + +static GList * +gtk_cell_area_get_cells (GtkCellLayout *cell_layout) +{ + GList *cells = NULL; + + gtk_cell_area_foreach (GTK_CELL_AREA (cell_layout), + (GtkCellCallback)accum_cells, + &cells); + + return g_list_reverse (cells); +} + +static GtkCellArea * +gtk_cell_area_get_area (GtkCellLayout *cell_layout) +{ + return GTK_CELL_AREA (cell_layout); +} + +/************************************************************* + * GtkBuildableIface * + *************************************************************/ +static void +gtk_cell_area_buildable_init (GtkBuildableIface *iface) +{ + iface->add_child = _gtk_cell_layout_buildable_add_child; + iface->custom_tag_start = _gtk_cell_layout_buildable_custom_tag_start; + iface->custom_tag_end = gtk_cell_area_buildable_custom_tag_end; +} + +static void +gtk_cell_area_buildable_custom_tag_end (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const char *tagname, + gpointer data) +{ + /* Just ignore the boolean return from here */ + _gtk_cell_layout_buildable_custom_tag_end (buildable, builder, child, tagname, data); +} + +/************************************************************* + * API * + *************************************************************/ + +/** + * gtk_cell_area_add: + * @area: a `GtkCellArea` + * @renderer: the `GtkCellRenderer` to add to @area + * + * Adds @renderer to @area with the default child cell properties. + * + * Deprecated: 4.10 + */ +void +gtk_cell_area_add (GtkCellArea *area, + GtkCellRenderer *renderer) +{ + g_return_if_fail (GTK_IS_CELL_AREA (area)); + g_return_if_fail (GTK_IS_CELL_RENDERER (renderer)); + + GTK_CELL_AREA_GET_CLASS (area)->add (area, renderer); +} + +/** + * gtk_cell_area_remove: + * @area: a `GtkCellArea` + * @renderer: the `GtkCellRenderer` to remove from @area + * + * Removes @renderer from @area. + * + * Deprecated: 4.10 + */ +void +gtk_cell_area_remove (GtkCellArea *area, + GtkCellRenderer *renderer) +{ + GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); + GList *renderers, *l; + + g_return_if_fail (GTK_IS_CELL_AREA (area)); + g_return_if_fail (GTK_IS_CELL_RENDERER (renderer)); + + /* Remove any custom attributes and custom cell data func here first */ + g_hash_table_remove (priv->cell_info, renderer); + + /* Remove focus siblings of this renderer */ + g_hash_table_remove (priv->focus_siblings, renderer); + + /* Remove this renderer from any focus renderer's sibling list */ + renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (area)); + + for (l = renderers; l; l = l->next) + { + GtkCellRenderer *focus_renderer = l->data; + + if (gtk_cell_area_is_focus_sibling (area, focus_renderer, renderer)) + { + gtk_cell_area_remove_focus_sibling (area, focus_renderer, renderer); + break; + } + } + + g_list_free (renderers); + + GTK_CELL_AREA_GET_CLASS (area)->remove (area, renderer); +} + +static gboolean +get_has_renderer (GtkCellRenderer *renderer, + HasRendererCheck *check) +{ + if (renderer == check->renderer) + check->has_renderer = TRUE; + + return check->has_renderer; +} + +/** + * gtk_cell_area_has_renderer: + * @area: a `GtkCellArea` + * @renderer: the `GtkCellRenderer` to check + * + * Checks if @area contains @renderer. + * + * Returns: %TRUE if @renderer is in the @area. + * + * Deprecated: 4.10 + */ +gboolean +gtk_cell_area_has_renderer (GtkCellArea *area, + GtkCellRenderer *renderer) +{ + HasRendererCheck check = { renderer, FALSE }; + + g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE); + g_return_val_if_fail (GTK_IS_CELL_RENDERER (renderer), FALSE); + + gtk_cell_area_foreach (area, (GtkCellCallback)get_has_renderer, &check); + + return check.has_renderer; +} + +/** + * gtk_cell_area_foreach: + * @area: a `GtkCellArea` + * @callback: (scope call): the `GtkCellCallback` to call + * @callback_data: user provided data pointer + * + * Calls @callback for every `GtkCellRenderer` in @area. + * + * Deprecated: 4.10 + */ +void +gtk_cell_area_foreach (GtkCellArea *area, + GtkCellCallback callback, + gpointer callback_data) +{ + g_return_if_fail (GTK_IS_CELL_AREA (area)); + g_return_if_fail (callback != NULL); + + GTK_CELL_AREA_GET_CLASS (area)->foreach (area, callback, callback_data); +} + +/** + * gtk_cell_area_foreach_alloc: + * @area: a `GtkCellArea` + * @context: the `GtkCellArea`Context for this row of data. + * @widget: the `GtkWidget` that @area is rendering to + * @cell_area: the @widget relative coordinates and size for @area + * @background_area: the @widget relative coordinates of the background area + * @callback: (scope call): the `GtkCellAllocCallback` to call + * @callback_data: user provided data pointer + * + * Calls @callback for every `GtkCellRenderer` in @area with the + * allocated rectangle inside @cell_area. + */ +void +gtk_cell_area_foreach_alloc (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + const GdkRectangle *cell_area, + const GdkRectangle *background_area, + GtkCellAllocCallback callback, + gpointer callback_data) +{ + g_return_if_fail (GTK_IS_CELL_AREA (area)); + g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context)); + g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (cell_area != NULL); + g_return_if_fail (callback != NULL); + + GTK_CELL_AREA_GET_CLASS (area)->foreach_alloc (area, context, widget, + cell_area, background_area, + callback, callback_data); +} + +/** + * gtk_cell_area_event: + * @area: a `GtkCellArea` + * @context: the `GtkCellArea`Context for this row of data. + * @widget: the `GtkWidget` that @area is rendering to + * @event: the `GdkEvent` to handle + * @cell_area: the @widget relative coordinates for @area + * @flags: the `GtkCellRenderer`State for @area in this row. + * + * Delegates event handling to a `GtkCellArea`. + * + * Returns: %TRUE if the event was handled by @area. + * + * Deprecated: 4.10 + */ +int +gtk_cell_area_event (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + GdkEvent *event, + const GdkRectangle *cell_area, + GtkCellRendererState flags) +{ + GtkCellAreaClass *class; + + g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0); + g_return_val_if_fail (GTK_IS_CELL_AREA_CONTEXT (context), 0); + g_return_val_if_fail (GTK_IS_WIDGET (widget), 0); + g_return_val_if_fail (event != NULL, 0); + g_return_val_if_fail (cell_area != NULL, 0); + + class = GTK_CELL_AREA_GET_CLASS (area); + + if (class->event) + return class->event (area, context, widget, event, cell_area, flags); + + g_warning ("GtkCellAreaClass::event not implemented for '%s'", + g_type_name (G_TYPE_FROM_INSTANCE (area))); + return 0; +} + +/** + * gtk_cell_area_snapshot: + * @area: a `GtkCellArea` + * @context: the `GtkCellArea`Context for this row of data. + * @widget: the `GtkWidget` that @area is rendering to + * @snapshot: the `GtkSnapshot` to draw to + * @background_area: the @widget relative coordinates for @area’s background + * @cell_area: the @widget relative coordinates for @area + * @flags: the `GtkCellRenderer`State for @area in this row. + * @paint_focus: whether @area should paint focus on focused cells for focused rows or not. + * + * Snapshots @area’s cells according to @area’s layout onto at + * the given coordinates. + * + * Deprecated: 4.10 + */ +void +gtk_cell_area_snapshot (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + GtkSnapshot *snapshot, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + GtkCellRendererState flags, + gboolean paint_focus) +{ + GtkCellAreaClass *class; + + g_return_if_fail (GTK_IS_CELL_AREA (area)); + g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context)); + g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (snapshot != NULL); + g_return_if_fail (background_area != NULL); + g_return_if_fail (cell_area != NULL); + + class = GTK_CELL_AREA_GET_CLASS (area); + + if (class->snapshot) + class->snapshot (area, context, widget, snapshot, background_area, cell_area, flags, paint_focus); + else + g_warning ("GtkCellAreaClass::snapshot not implemented for '%s'", + g_type_name (G_TYPE_FROM_INSTANCE (area))); +} + +static gboolean +get_cell_allocation (GtkCellRenderer *renderer, + const GdkRectangle *cell_area, + const GdkRectangle *cell_background, + RendererAllocationData *data) +{ + if (data->renderer == renderer) + data->allocation = *cell_area; + + return (data->renderer == renderer); +} + +/** + * gtk_cell_area_get_cell_allocation: + * @area: a `GtkCellArea` + * @context: the `GtkCellArea`Context used to hold sizes for @area. + * @widget: the `GtkWidget` that @area is rendering on + * @renderer: the `GtkCellRenderer` to get the allocation for + * @cell_area: the whole allocated area for @area in @widget + * for this row + * @allocation: (out): where to store the allocation for @renderer + * + * Derives the allocation of @renderer inside @area if @area + * were to be renderered in @cell_area. + * + * Deprecated: 4.10 + */ +void +gtk_cell_area_get_cell_allocation (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + GtkCellRenderer *renderer, + const GdkRectangle *cell_area, + GdkRectangle *allocation) +{ + RendererAllocationData data = { renderer, { 0, } }; + + g_return_if_fail (GTK_IS_CELL_AREA (area)); + g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context)); + g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (GTK_IS_CELL_RENDERER (renderer)); + g_return_if_fail (cell_area != NULL); + g_return_if_fail (allocation != NULL); + + gtk_cell_area_foreach_alloc (area, context, widget, cell_area, cell_area, + (GtkCellAllocCallback)get_cell_allocation, &data); + + *allocation = data.allocation; +} + +static gboolean +get_cell_by_position (GtkCellRenderer *renderer, + const GdkRectangle *cell_area, + const GdkRectangle *cell_background, + CellByPositionData *data) +{ + if (data->x >= cell_area->x && data->x < cell_area->x + cell_area->width && + data->y >= cell_area->y && data->y < cell_area->y + cell_area->height) + { + data->renderer = renderer; + data->cell_area = *cell_area; + } + + return (data->renderer != NULL); +} + +/** + * gtk_cell_area_get_cell_at_position: + * @area: a `GtkCellArea` + * @context: the `GtkCellArea`Context used to hold sizes for @area. + * @widget: the `GtkWidget` that @area is rendering on + * @cell_area: the whole allocated area for @area in @widget + * for this row + * @x: the x position + * @y: the y position + * @alloc_area: (out) (optional): where to store the inner allocated area of the + * returned cell renderer + * + * Gets the `GtkCellRenderer` at @x and @y coordinates inside @area and optionally + * returns the full cell allocation for it inside @cell_area. + * + * Returns: (transfer none): the `GtkCellRenderer` at @x and @y. + * + * Deprecated: 4.10 + */ +GtkCellRenderer * +gtk_cell_area_get_cell_at_position (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + const GdkRectangle *cell_area, + int x, + int y, + GdkRectangle *alloc_area) +{ + CellByPositionData data = { x, y, NULL, { 0, } }; + + g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL); + g_return_val_if_fail (GTK_IS_CELL_AREA_CONTEXT (context), NULL); + g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL); + g_return_val_if_fail (cell_area != NULL, NULL); + g_return_val_if_fail (x >= cell_area->x && x <= cell_area->x + cell_area->width, NULL); + g_return_val_if_fail (y >= cell_area->y && y <= cell_area->y + cell_area->height, NULL); + + gtk_cell_area_foreach_alloc (area, context, widget, cell_area, cell_area, + (GtkCellAllocCallback)get_cell_by_position, &data); + + if (alloc_area) + *alloc_area = data.cell_area; + + return data.renderer; +} + +/************************************************************* + * API: Geometry * + *************************************************************/ +/** + * gtk_cell_area_create_context: + * @area: a `GtkCellArea` + * + * Creates a `GtkCellArea`Context to be used with @area for + * all purposes. `GtkCellArea`Context stores geometry information + * for rows for which it was operated on, it is important to use + * the same context for the same row of data at all times (i.e. + * one should render and handle events with the same `GtkCellArea`Context + * which was used to request the size of those rows of data). + * + * Returns: (transfer full): a newly created `GtkCellArea`Context which can be used with @area. + * + * Deprecated: 4.10 + */ +GtkCellAreaContext * +gtk_cell_area_create_context (GtkCellArea *area) +{ + g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL); + + return GTK_CELL_AREA_GET_CLASS (area)->create_context (area); +} + +/** + * gtk_cell_area_copy_context: + * @area: a `GtkCellArea` + * @context: the `GtkCellArea`Context to copy + * + * This is sometimes needed for cases where rows need to share + * alignments in one orientation but may be separately grouped + * in the opposing orientation. + * + * For instance, `GtkIconView` creates all icons (rows) to have + * the same width and the cells theirin to have the same + * horizontal alignments. However each row of icons may have + * a separate collective height. `GtkIconView` uses this to + * request the heights of each row based on a context which + * was already used to request all the row widths that are + * to be displayed. + * + * Returns: (transfer full): a newly created `GtkCellArea`Context copy of @context. + * + * Deprecated: 4.10 + */ +GtkCellAreaContext * +gtk_cell_area_copy_context (GtkCellArea *area, + GtkCellAreaContext *context) +{ + g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL); + g_return_val_if_fail (GTK_IS_CELL_AREA_CONTEXT (context), NULL); + + return GTK_CELL_AREA_GET_CLASS (area)->copy_context (area, context); +} + +/** + * gtk_cell_area_get_request_mode: + * @area: a `GtkCellArea` + * + * Gets whether the area prefers a height-for-width layout + * or a width-for-height layout. + * + * Returns: The `GtkSizeRequestMode` preferred by @area. + */ +GtkSizeRequestMode +gtk_cell_area_get_request_mode (GtkCellArea *area) +{ + g_return_val_if_fail (GTK_IS_CELL_AREA (area), + GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH); + + return GTK_CELL_AREA_GET_CLASS (area)->get_request_mode (area); +} + +/** + * gtk_cell_area_get_preferred_width: + * @area: a `GtkCellArea` + * @context: the `GtkCellArea`Context to perform this request with + * @widget: the `GtkWidget` where @area will be rendering + * @minimum_width: (out) (optional): location to store the minimum width + * @natural_width: (out) (optional): location to store the natural width + * + * Retrieves a cell area’s initial minimum and natural width. + * + * @area will store some geometrical information in @context along the way; + * when requesting sizes over an arbitrary number of rows, it’s not important + * to check the @minimum_width and @natural_width of this call but rather to + * consult gtk_cell_area_context_get_preferred_width() after a series of + * requests. + * + * Deprecated: 4.10 + */ +void +gtk_cell_area_get_preferred_width (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + int *minimum_width, + int *natural_width) +{ + g_return_if_fail (GTK_IS_CELL_AREA (area)); + g_return_if_fail (GTK_IS_WIDGET (widget)); + + GTK_CELL_AREA_GET_CLASS (area)->get_preferred_width (area, context, widget, + minimum_width, natural_width); +} + +/** + * gtk_cell_area_get_preferred_height_for_width: + * @area: a `GtkCellArea` + * @context: the `GtkCellArea`Context which has already been requested for widths. + * @widget: the `GtkWidget` where @area will be rendering + * @width: the width for which to check the height of this area + * @minimum_height: (out) (optional): location to store the minimum height + * @natural_height: (out) (optional): location to store the natural height + * + * Retrieves a cell area’s minimum and natural height if it would be given + * the specified @width. + * + * @area stores some geometrical information in @context along the way + * while calling gtk_cell_area_get_preferred_width(). It’s important to + * perform a series of gtk_cell_area_get_preferred_width() requests with + * @context first and then call gtk_cell_area_get_preferred_height_for_width() + * on each cell area individually to get the height for width of each + * fully requested row. + * + * If at some point, the width of a single row changes, it should be + * requested with gtk_cell_area_get_preferred_width() again and then + * the full width of the requested rows checked again with + * gtk_cell_area_context_get_preferred_width(). + * + * Deprecated: 4.10 + */ +void +gtk_cell_area_get_preferred_height_for_width (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + int width, + int *minimum_height, + int *natural_height) +{ + GtkCellAreaClass *class; + + g_return_if_fail (GTK_IS_CELL_AREA (area)); + g_return_if_fail (GTK_IS_WIDGET (widget)); + + class = GTK_CELL_AREA_GET_CLASS (area); + class->get_preferred_height_for_width (area, context, widget, width, minimum_height, natural_height); +} + + +/** + * gtk_cell_area_get_preferred_height: + * @area: a `GtkCellArea` + * @context: the `GtkCellArea`Context to perform this request with + * @widget: the `GtkWidget` where @area will be rendering + * @minimum_height: (out) (optional): location to store the minimum height + * @natural_height: (out) (optional): location to store the natural height + * + * Retrieves a cell area’s initial minimum and natural height. + * + * @area will store some geometrical information in @context along the way; + * when requesting sizes over an arbitrary number of rows, it’s not important + * to check the @minimum_height and @natural_height of this call but rather to + * consult gtk_cell_area_context_get_preferred_height() after a series of + * requests. + * + * Deprecated: 4.10 + */ +void +gtk_cell_area_get_preferred_height (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + int *minimum_height, + int *natural_height) +{ + g_return_if_fail (GTK_IS_CELL_AREA (area)); + g_return_if_fail (GTK_IS_WIDGET (widget)); + + GTK_CELL_AREA_GET_CLASS (area)->get_preferred_height (area, context, widget, + minimum_height, natural_height); +} + +/** + * gtk_cell_area_get_preferred_width_for_height: + * @area: a `GtkCellArea` + * @context: the `GtkCellArea`Context which has already been requested for widths. + * @widget: the `GtkWidget` where @area will be rendering + * @height: the height for which to check the width of this area + * @minimum_width: (out) (optional): location to store the minimum width + * @natural_width: (out) (optional): location to store the natural width + * + * Retrieves a cell area’s minimum and natural width if it would be given + * the specified @height. + * + * @area stores some geometrical information in @context along the way + * while calling gtk_cell_area_get_preferred_height(). It’s important to + * perform a series of gtk_cell_area_get_preferred_height() requests with + * @context first and then call gtk_cell_area_get_preferred_width_for_height() + * on each cell area individually to get the height for width of each + * fully requested row. + * + * If at some point, the height of a single row changes, it should be + * requested with gtk_cell_area_get_preferred_height() again and then + * the full height of the requested rows checked again with + * gtk_cell_area_context_get_preferred_height(). + * + * Deprecated: 4.10 + */ +void +gtk_cell_area_get_preferred_width_for_height (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + int height, + int *minimum_width, + int *natural_width) +{ + GtkCellAreaClass *class; + + g_return_if_fail (GTK_IS_CELL_AREA (area)); + g_return_if_fail (GTK_IS_WIDGET (widget)); + + class = GTK_CELL_AREA_GET_CLASS (area); + class->get_preferred_width_for_height (area, context, widget, height, minimum_width, natural_width); +} + +/************************************************************* + * API: Attributes * + *************************************************************/ + +/** + * gtk_cell_area_attribute_connect: + * @area: a `GtkCellArea` + * @renderer: the `GtkCellRenderer` to connect an attribute for + * @attribute: the attribute name + * @column: the `GtkTreeModel` column to fetch attribute values from + * + * Connects an @attribute to apply values from @column for the + * `GtkTreeModel` in use. + * + * Deprecated: 4.10 + */ +void +gtk_cell_area_attribute_connect (GtkCellArea *area, + GtkCellRenderer *renderer, + const char *attribute, + int column) +{ + GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); + CellInfo *info; + CellAttribute *cell_attribute; + + g_return_if_fail (GTK_IS_CELL_AREA (area)); + g_return_if_fail (GTK_IS_CELL_RENDERER (renderer)); + g_return_if_fail (attribute != NULL); + g_return_if_fail (gtk_cell_area_has_renderer (area, renderer)); + + info = g_hash_table_lookup (priv->cell_info, renderer); + + if (!info) + { + info = cell_info_new (NULL, NULL, NULL); + + g_hash_table_insert (priv->cell_info, renderer, info); + } + else + { + GSList *node; + + /* Check we are not adding the same attribute twice */ + if ((node = g_slist_find_custom (info->attributes, attribute, + (GCompareFunc)cell_attribute_find)) != NULL) + { + cell_attribute = node->data; + + g_warning ("Cannot connect attribute '%s' for cell renderer class '%s' " + "since '%s' is already attributed to column %d", + attribute, + G_OBJECT_TYPE_NAME (renderer), + attribute, cell_attribute->column); + return; + } + } + + cell_attribute = cell_attribute_new (renderer, attribute, column); + + if (!cell_attribute) + { + g_warning ("Cannot connect attribute '%s' for cell renderer class '%s' " + "since attribute does not exist", + attribute, + G_OBJECT_TYPE_NAME (renderer)); + return; + } + + info->attributes = g_slist_prepend (info->attributes, cell_attribute); +} + +/** + * gtk_cell_area_attribute_disconnect: + * @area: a `GtkCellArea` + * @renderer: the `GtkCellRenderer` to disconnect an attribute for + * @attribute: the attribute name + * + * Disconnects @attribute for the @renderer in @area so that + * attribute will no longer be updated with values from the + * model. + * + * Deprecated: 4.10 + */ +void +gtk_cell_area_attribute_disconnect (GtkCellArea *area, + GtkCellRenderer *renderer, + const char *attribute) +{ + GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); + CellInfo *info; + CellAttribute *cell_attribute; + GSList *node; + + g_return_if_fail (GTK_IS_CELL_AREA (area)); + g_return_if_fail (GTK_IS_CELL_RENDERER (renderer)); + g_return_if_fail (attribute != NULL); + g_return_if_fail (gtk_cell_area_has_renderer (area, renderer)); + + info = g_hash_table_lookup (priv->cell_info, renderer); + + if (info) + { + node = g_slist_find_custom (info->attributes, attribute, + (GCompareFunc)cell_attribute_find); + if (node) + { + cell_attribute = node->data; + + cell_attribute_free (cell_attribute); + + info->attributes = g_slist_delete_link (info->attributes, node); + } + } +} + +/** + * gtk_cell_area_attribute_get_column: + * @area: a `GtkCellArea` + * @renderer: a `GtkCellRenderer` + * @attribute: an attribute on the renderer + * + * Returns the model column that an attribute has been mapped to, + * or -1 if the attribute is not mapped. + * + * Returns: the model column, or -1 + * + * Deprecated: 4.10 + */ +int +gtk_cell_area_attribute_get_column (GtkCellArea *area, + GtkCellRenderer *renderer, + const char *attribute) +{ + GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); + CellInfo *info; + CellAttribute *cell_attribute; + GSList *node; + + info = g_hash_table_lookup (priv->cell_info, renderer); + + if (info) + { + node = g_slist_find_custom (info->attributes, attribute, + (GCompareFunc)cell_attribute_find); + if (node) + { + cell_attribute = node->data; + return cell_attribute->column; + } + } + + return -1; +} + +/** + * gtk_cell_area_apply_attributes: + * @area: a `GtkCellArea` + * @tree_model: the `GtkTreeModel` to pull values from + * @iter: the `GtkTreeIter` in @tree_model to apply values for + * @is_expander: whether @iter has children + * @is_expanded: whether @iter is expanded in the view and + * children are visible + * + * Applies any connected attributes to the renderers in + * @area by pulling the values from @tree_model. + * + * Deprecated: 4.10 + */ +void +gtk_cell_area_apply_attributes (GtkCellArea *area, + GtkTreeModel *tree_model, + GtkTreeIter *iter, + gboolean is_expander, + gboolean is_expanded) +{ + g_return_if_fail (GTK_IS_CELL_AREA (area)); + g_return_if_fail (GTK_IS_TREE_MODEL (tree_model)); + g_return_if_fail (iter != NULL); + + g_signal_emit (area, cell_area_signals[SIGNAL_APPLY_ATTRIBUTES], 0, + tree_model, iter, is_expander, is_expanded); +} + +/** + * gtk_cell_area_get_current_path_string: + * @area: a `GtkCellArea` + * + * Gets the current `GtkTreePath` string for the currently + * applied `GtkTreeIter`, this is implicitly updated when + * gtk_cell_area_apply_attributes() is called and can be + * used to interact with renderers from `GtkCellArea` + * subclasses. + * + * Returns: The current `GtkTreePath` string for the current + * attributes applied to @area. This string belongs to the area and + * should not be freed. + */ +const char * +gtk_cell_area_get_current_path_string (GtkCellArea *area) +{ + GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); + + g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL); + + return priv->current_path; +} + + +/************************************************************* + * API: Cell Properties * + *************************************************************/ +/** + * gtk_cell_area_class_install_cell_property: + * @aclass: a `GtkCellAreaClass` + * @property_id: the id for the property + * @pspec: the `GParamSpec` for the property + * + * Installs a cell property on a cell area class. + * + * Deprecated: 4.10 + */ +void +gtk_cell_area_class_install_cell_property (GtkCellAreaClass *aclass, + guint property_id, + GParamSpec *pspec) +{ + g_return_if_fail (GTK_IS_CELL_AREA_CLASS (aclass)); + g_return_if_fail (G_IS_PARAM_SPEC (pspec)); + if (pspec->flags & G_PARAM_WRITABLE) + g_return_if_fail (aclass->set_cell_property != NULL); + if (pspec->flags & G_PARAM_READABLE) + g_return_if_fail (aclass->get_cell_property != NULL); + g_return_if_fail (property_id > 0); + g_return_if_fail (PARAM_SPEC_PARAM_ID (pspec) == 0); /* paranoid */ + g_return_if_fail ((pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)) == 0); + + if (g_param_spec_pool_lookup (cell_property_pool, pspec->name, G_OBJECT_CLASS_TYPE (aclass), TRUE)) + { + g_warning (G_STRLOC ": class '%s' already contains a cell property named '%s'", + G_OBJECT_CLASS_NAME (aclass), pspec->name); + return; + } + g_param_spec_ref (pspec); + g_param_spec_sink (pspec); + PARAM_SPEC_SET_PARAM_ID (pspec, property_id); + g_param_spec_pool_insert (cell_property_pool, pspec, G_OBJECT_CLASS_TYPE (aclass)); +} + +/** + * gtk_cell_area_class_find_cell_property: + * @aclass: a `GtkCellAreaClass` + * @property_name: the name of the child property to find + * + * Finds a cell property of a cell area class by name. + * + * Returns: (transfer none): the `GParamSpec` of the child property + * + * Deprecated: 4.10 + */ +GParamSpec* +gtk_cell_area_class_find_cell_property (GtkCellAreaClass *aclass, + const char *property_name) +{ + g_return_val_if_fail (GTK_IS_CELL_AREA_CLASS (aclass), NULL); + g_return_val_if_fail (property_name != NULL, NULL); + + return g_param_spec_pool_lookup (cell_property_pool, + property_name, + G_OBJECT_CLASS_TYPE (aclass), + TRUE); +} + +/** + * gtk_cell_area_class_list_cell_properties: + * @aclass: a `GtkCellAreaClass` + * @n_properties: (out): location to return the number of cell properties found + * + * Returns all cell properties of a cell area class. + * + * Returns: (array length=n_properties) (transfer container): a newly + * allocated %NULL-terminated array of `GParamSpec`*. The array + * must be freed with g_free(). + * + * Deprecated: 4.10 + */ +GParamSpec** +gtk_cell_area_class_list_cell_properties (GtkCellAreaClass *aclass, + guint *n_properties) +{ + GParamSpec **pspecs; + guint n; + + g_return_val_if_fail (GTK_IS_CELL_AREA_CLASS (aclass), NULL); + + pspecs = g_param_spec_pool_list (cell_property_pool, + G_OBJECT_CLASS_TYPE (aclass), + &n); + if (n_properties) + *n_properties = n; + + return pspecs; +} + +/** + * gtk_cell_area_add_with_properties: + * @area: a `GtkCellArea` + * @renderer: a `GtkCellRenderer` to be placed inside @area + * @first_prop_name: the name of the first cell property to set + * @...: a %NULL-terminated list of property names and values, starting + * with @first_prop_name + * + * Adds @renderer to @area, setting cell properties at the same time. + * See gtk_cell_area_add() and gtk_cell_area_cell_set() for more details. + * + * Deprecated: 4.10 + */ +void +gtk_cell_area_add_with_properties (GtkCellArea *area, + GtkCellRenderer *renderer, + const char *first_prop_name, + ...) +{ + GtkCellAreaClass *class; + + g_return_if_fail (GTK_IS_CELL_AREA (area)); + g_return_if_fail (GTK_IS_CELL_RENDERER (renderer)); + + class = GTK_CELL_AREA_GET_CLASS (area); + + if (class->add) + { + va_list var_args; + + class->add (area, renderer); + + va_start (var_args, first_prop_name); + gtk_cell_area_cell_set_valist (area, renderer, first_prop_name, var_args); + va_end (var_args); + } + else + g_warning ("GtkCellAreaClass::add not implemented for '%s'", + g_type_name (G_TYPE_FROM_INSTANCE (area))); +} + +/** + * gtk_cell_area_cell_set: + * @area: a `GtkCellArea` + * @renderer: a `GtkCellRenderer` which is a cell inside @area + * @first_prop_name: the name of the first cell property to set + * @...: a %NULL-terminated list of property names and values, starting + * with @first_prop_name + * + * Sets one or more cell properties for @cell in @area. + * + * Deprecated: 4.10 + */ +void +gtk_cell_area_cell_set (GtkCellArea *area, + GtkCellRenderer *renderer, + const char *first_prop_name, + ...) +{ + va_list var_args; + + g_return_if_fail (GTK_IS_CELL_AREA (area)); + g_return_if_fail (GTK_IS_CELL_RENDERER (renderer)); + + va_start (var_args, first_prop_name); + gtk_cell_area_cell_set_valist (area, renderer, first_prop_name, var_args); + va_end (var_args); +} + +/** + * gtk_cell_area_cell_get: + * @area: a `GtkCellArea` + * @renderer: a `GtkCellRenderer` which is inside @area + * @first_prop_name: the name of the first cell property to get + * @...: return location for the first cell property, followed + * optionally by more name/return location pairs, followed by %NULL + * + * Gets the values of one or more cell properties for @renderer in @area. + * + * Deprecated: 4.10 + */ +void +gtk_cell_area_cell_get (GtkCellArea *area, + GtkCellRenderer *renderer, + const char *first_prop_name, + ...) +{ + va_list var_args; + + g_return_if_fail (GTK_IS_CELL_AREA (area)); + g_return_if_fail (GTK_IS_CELL_RENDERER (renderer)); + + va_start (var_args, first_prop_name); + gtk_cell_area_cell_get_valist (area, renderer, first_prop_name, var_args); + va_end (var_args); +} + +static inline void +area_get_cell_property (GtkCellArea *area, + GtkCellRenderer *renderer, + GParamSpec *pspec, + GValue *value) +{ + GtkCellAreaClass *class = g_type_class_peek (pspec->owner_type); + + class->get_cell_property (area, renderer, PARAM_SPEC_PARAM_ID (pspec), value, pspec); +} + +static inline void +area_set_cell_property (GtkCellArea *area, + GtkCellRenderer *renderer, + GParamSpec *pspec, + const GValue *value) +{ + GValue tmp_value = G_VALUE_INIT; + GtkCellAreaClass *class = g_type_class_peek (pspec->owner_type); + + /* provide a copy to work from, convert (if necessary) and validate */ + g_value_init (&tmp_value, G_PARAM_SPEC_VALUE_TYPE (pspec)); + if (!g_value_transform (value, &tmp_value)) + g_warning ("unable to set cell property '%s' of type '%s' from value of type '%s'", + pspec->name, + g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)), + G_VALUE_TYPE_NAME (value)); + else if (g_param_value_validate (pspec, &tmp_value) && !(pspec->flags & G_PARAM_LAX_VALIDATION)) + { + char *contents = g_strdup_value_contents (value); + + g_warning ("value \"%s\" of type '%s' is invalid for property '%s' of type '%s'", + contents, + G_VALUE_TYPE_NAME (value), + pspec->name, + g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec))); + g_free (contents); + } + else + { + class->set_cell_property (area, renderer, PARAM_SPEC_PARAM_ID (pspec), &tmp_value, pspec); + } + g_value_unset (&tmp_value); +} + +/** + * gtk_cell_area_cell_set_valist: + * @area: a `GtkCellArea` + * @renderer: a `GtkCellRenderer` which inside @area + * @first_property_name: the name of the first cell property to set + * @var_args: a %NULL-terminated list of property names and values, starting + * with @first_prop_name + * + * Sets one or more cell properties for @renderer in @area. + * + * Deprecated: 4.10 + */ +void +gtk_cell_area_cell_set_valist (GtkCellArea *area, + GtkCellRenderer *renderer, + const char *first_property_name, + va_list var_args) +{ + const char *name; + + g_return_if_fail (GTK_IS_CELL_AREA (area)); + g_return_if_fail (GTK_IS_CELL_RENDERER (renderer)); + + name = first_property_name; + while (name) + { + GValue value = G_VALUE_INIT; + char *error = NULL; + GParamSpec *pspec = + g_param_spec_pool_lookup (cell_property_pool, name, + G_OBJECT_TYPE (area), TRUE); + if (!pspec) + { + g_warning ("%s: cell area class '%s' has no cell property named '%s'", + G_STRLOC, G_OBJECT_TYPE_NAME (area), name); + break; + } + if (!(pspec->flags & G_PARAM_WRITABLE)) + { + g_warning ("%s: cell property '%s' of cell area class '%s' is not writable", + G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area)); + break; + } + + G_VALUE_COLLECT_INIT (&value, G_PARAM_SPEC_VALUE_TYPE (pspec), + var_args, 0, &error); + if (error) + { + g_warning ("%s: %s", G_STRLOC, error); + g_free (error); + + /* we purposely leak the value here, it might not be + * in a sane state if an error condition occurred + */ + break; + } + area_set_cell_property (area, renderer, pspec, &value); + g_value_unset (&value); + name = va_arg (var_args, char *); + } +} + +/** + * gtk_cell_area_cell_get_valist: + * @area: a `GtkCellArea` + * @renderer: a `GtkCellRenderer` inside @area + * @first_property_name: the name of the first property to get + * @var_args: return location for the first property, followed + * optionally by more name/return location pairs, followed by %NULL + * + * Gets the values of one or more cell properties for @renderer in @area. + * + * Deprecated: 4.10 + */ +void +gtk_cell_area_cell_get_valist (GtkCellArea *area, + GtkCellRenderer *renderer, + const char *first_property_name, + va_list var_args) +{ + const char *name; + + g_return_if_fail (GTK_IS_CELL_AREA (area)); + g_return_if_fail (GTK_IS_CELL_RENDERER (renderer)); + + name = first_property_name; + while (name) + { + GValue value = G_VALUE_INIT; + GParamSpec *pspec; + char *error; + + pspec = g_param_spec_pool_lookup (cell_property_pool, name, + G_OBJECT_TYPE (area), TRUE); + if (!pspec) + { + g_warning ("%s: cell area class '%s' has no cell property named '%s'", + G_STRLOC, G_OBJECT_TYPE_NAME (area), name); + break; + } + if (!(pspec->flags & G_PARAM_READABLE)) + { + g_warning ("%s: cell property '%s' of cell area class '%s' is not readable", + G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area)); + break; + } + + g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec)); + area_get_cell_property (area, renderer, pspec, &value); + G_VALUE_LCOPY (&value, var_args, 0, &error); + if (error) + { + g_warning ("%s: %s", G_STRLOC, error); + g_free (error); + g_value_unset (&value); + break; + } + g_value_unset (&value); + name = va_arg (var_args, char *); + } +} + +/** + * gtk_cell_area_cell_set_property: + * @area: a `GtkCellArea` + * @renderer: a `GtkCellRenderer` inside @area + * @property_name: the name of the cell property to set + * @value: the value to set the cell property to + * + * Sets a cell property for @renderer in @area. + * + * Deprecated: 4.10 + */ +void +gtk_cell_area_cell_set_property (GtkCellArea *area, + GtkCellRenderer *renderer, + const char *property_name, + const GValue *value) +{ + GParamSpec *pspec; + + g_return_if_fail (GTK_IS_CELL_AREA (area)); + g_return_if_fail (GTK_IS_CELL_RENDERER (renderer)); + g_return_if_fail (property_name != NULL); + g_return_if_fail (G_IS_VALUE (value)); + + pspec = g_param_spec_pool_lookup (cell_property_pool, property_name, + G_OBJECT_TYPE (area), TRUE); + if (!pspec) + g_warning ("%s: cell area class '%s' has no cell property named '%s'", + G_STRLOC, G_OBJECT_TYPE_NAME (area), property_name); + else if (!(pspec->flags & G_PARAM_WRITABLE)) + g_warning ("%s: cell property '%s' of cell area class '%s' is not writable", + G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area)); + else + { + area_set_cell_property (area, renderer, pspec, value); + } +} + +/** + * gtk_cell_area_cell_get_property: + * @area: a `GtkCellArea` + * @renderer: a `GtkCellRenderer` inside @area + * @property_name: the name of the property to get + * @value: a location to return the value + * + * Gets the value of a cell property for @renderer in @area. + * + * Deprecated: 4.10 + */ +void +gtk_cell_area_cell_get_property (GtkCellArea *area, + GtkCellRenderer *renderer, + const char *property_name, + GValue *value) +{ + GParamSpec *pspec; + + g_return_if_fail (GTK_IS_CELL_AREA (area)); + g_return_if_fail (GTK_IS_CELL_RENDERER (renderer)); + g_return_if_fail (property_name != NULL); + g_return_if_fail (G_IS_VALUE (value)); + + pspec = g_param_spec_pool_lookup (cell_property_pool, property_name, + G_OBJECT_TYPE (area), TRUE); + if (!pspec) + g_warning ("%s: cell area class '%s' has no cell property named '%s'", + G_STRLOC, G_OBJECT_TYPE_NAME (area), property_name); + else if (!(pspec->flags & G_PARAM_READABLE)) + g_warning ("%s: cell property '%s' of cell area class '%s' is not readable", + G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area)); + else + { + GValue *prop_value, tmp_value = G_VALUE_INIT; + + /* auto-conversion of the callers value type + */ + if (G_VALUE_TYPE (value) == G_PARAM_SPEC_VALUE_TYPE (pspec)) + { + g_value_reset (value); + prop_value = value; + } + else if (!g_value_type_transformable (G_PARAM_SPEC_VALUE_TYPE (pspec), G_VALUE_TYPE (value))) + { + g_warning ("can't retrieve cell property '%s' of type '%s' as value of type '%s'", + pspec->name, + g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)), + G_VALUE_TYPE_NAME (value)); + return; + } + else + { + g_value_init (&tmp_value, G_PARAM_SPEC_VALUE_TYPE (pspec)); + prop_value = &tmp_value; + } + + area_get_cell_property (area, renderer, pspec, prop_value); + + if (prop_value != value) + { + g_value_transform (prop_value, value); + g_value_unset (&tmp_value); + } + } +} + +/************************************************************* + * API: Focus * + *************************************************************/ + +/** + * gtk_cell_area_is_activatable: + * @area: a `GtkCellArea` + * + * Returns whether the area can do anything when activated, + * after applying new attributes to @area. + * + * Returns: whether @area can do anything when activated. + * + * Deprecated: 4.10 + */ +gboolean +gtk_cell_area_is_activatable (GtkCellArea *area) +{ + g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE); + + return GTK_CELL_AREA_GET_CLASS (area)->is_activatable (area); +} + +/** + * gtk_cell_area_focus: + * @area: a `GtkCellArea` + * @direction: the `GtkDirectionType` + * + * This should be called by the @area’s owning layout widget + * when focus is to be passed to @area, or moved within @area + * for a given @direction and row data. + * + * Implementing `GtkCellArea` classes should implement this + * method to receive and navigate focus in its own way particular + * to how it lays out cells. + * + * Returns: %TRUE if focus remains inside @area as a result of this call. + * + * Deprecated: 4.10 + */ +gboolean +gtk_cell_area_focus (GtkCellArea *area, + GtkDirectionType direction) +{ + g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE); + + return GTK_CELL_AREA_GET_CLASS (area)->focus (area, direction); +} + +/** + * gtk_cell_area_activate: + * @area: a `GtkCellArea` + * @context: the `GtkCellArea`Context in context with the current row data + * @widget: the `GtkWidget` that @area is rendering on + * @cell_area: the size and location of @area relative to @widget’s allocation + * @flags: the `GtkCellRenderer`State flags for @area for this row of data. + * @edit_only: if %TRUE then only cell renderers that are %GTK_CELL_RENDERER_MODE_EDITABLE + * will be activated. + * + * Activates @area, usually by activating the currently focused + * cell, however some subclasses which embed widgets in the area + * can also activate a widget if it currently has the focus. + * + * Returns: Whether @area was successfully activated. + * + * Deprecated: 4.10 + */ +gboolean +gtk_cell_area_activate (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + const GdkRectangle *cell_area, + GtkCellRendererState flags, + gboolean edit_only) +{ + g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE); + + return GTK_CELL_AREA_GET_CLASS (area)->activate (area, context, widget, cell_area, flags, edit_only); +} + + +/** + * gtk_cell_area_set_focus_cell: + * @area: a `GtkCellArea` + * @renderer: (nullable): the `GtkCellRenderer` to give focus to + * + * Explicitly sets the currently focused cell to @renderer. + * + * This is generally called by implementations of + * `GtkCellAreaClass.focus()` or `GtkCellAreaClass.event()`, + * however it can also be used to implement functions such + * as gtk_tree_view_set_cursor_on_cell(). + * + * Deprecated: 4.10 + */ +void +gtk_cell_area_set_focus_cell (GtkCellArea *area, + GtkCellRenderer *renderer) +{ + GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); + + g_return_if_fail (GTK_IS_CELL_AREA (area)); + g_return_if_fail (renderer == NULL || GTK_IS_CELL_RENDERER (renderer)); + + if (priv->focus_cell != renderer) + { + if (priv->focus_cell) + g_object_unref (priv->focus_cell); + + priv->focus_cell = renderer; + + if (priv->focus_cell) + g_object_ref (priv->focus_cell); + + g_object_notify (G_OBJECT (area), "focus-cell"); + } + + /* Signal that the current focus renderer for this path changed + * (it may be that the focus cell did not change, but the row + * may have changed so we need to signal it) */ + g_signal_emit (area, cell_area_signals[SIGNAL_FOCUS_CHANGED], 0, + priv->focus_cell, priv->current_path); + +} + +/** + * gtk_cell_area_get_focus_cell: + * @area: a `GtkCellArea` + * + * Retrieves the currently focused cell for @area + * + * Returns: (transfer none) (nullable): the currently focused cell in @area. + * + * Deprecated: 4.10 + */ +GtkCellRenderer * +gtk_cell_area_get_focus_cell (GtkCellArea *area) +{ + GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); + + g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL); + + return priv->focus_cell; +} + + +/************************************************************* + * API: Focus Siblings * + *************************************************************/ + +/** + * gtk_cell_area_add_focus_sibling: + * @area: a `GtkCellArea` + * @renderer: the `GtkCellRenderer` expected to have focus + * @sibling: the `GtkCellRenderer` to add to @renderer’s focus area + * + * Adds @sibling to @renderer’s focusable area, focus will be drawn + * around @renderer and all of its siblings if @renderer can + * focus for a given row. + * + * Events handled by focus siblings can also activate the given + * focusable @renderer. + * + * Deprecated: 4.10 + */ +void +gtk_cell_area_add_focus_sibling (GtkCellArea *area, + GtkCellRenderer *renderer, + GtkCellRenderer *sibling) +{ + GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); + GList *siblings; + + g_return_if_fail (GTK_IS_CELL_AREA (area)); + g_return_if_fail (GTK_IS_CELL_RENDERER (renderer)); + g_return_if_fail (GTK_IS_CELL_RENDERER (sibling)); + g_return_if_fail (renderer != sibling); + g_return_if_fail (gtk_cell_area_has_renderer (area, renderer)); + g_return_if_fail (gtk_cell_area_has_renderer (area, sibling)); + g_return_if_fail (!gtk_cell_area_is_focus_sibling (area, renderer, sibling)); + + /* XXX We should also check that sibling is not in any other renderer's sibling + * list already, a renderer can be sibling of only one focusable renderer + * at a time. + */ + + siblings = g_hash_table_lookup (priv->focus_siblings, renderer); + + if (siblings) + { + G_GNUC_UNUSED GList *unused = g_list_append (siblings, sibling); + } + else + { + siblings = g_list_append (siblings, sibling); + g_hash_table_insert (priv->focus_siblings, renderer, siblings); + } +} + +/** + * gtk_cell_area_remove_focus_sibling: + * @area: a `GtkCellArea` + * @renderer: the `GtkCellRenderer` expected to have focus + * @sibling: the `GtkCellRenderer` to remove from @renderer’s focus area + * + * Removes @sibling from @renderer’s focus sibling list + * (see gtk_cell_area_add_focus_sibling()). + * + * Deprecated: 4.10 + */ +void +gtk_cell_area_remove_focus_sibling (GtkCellArea *area, + GtkCellRenderer *renderer, + GtkCellRenderer *sibling) +{ + GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); + GList *siblings; + + g_return_if_fail (GTK_IS_CELL_AREA (area)); + g_return_if_fail (GTK_IS_CELL_RENDERER (renderer)); + g_return_if_fail (GTK_IS_CELL_RENDERER (sibling)); + g_return_if_fail (gtk_cell_area_is_focus_sibling (area, renderer, sibling)); + + siblings = g_hash_table_lookup (priv->focus_siblings, renderer); + + siblings = g_list_copy (siblings); + siblings = g_list_remove (siblings, sibling); + + if (!siblings) + g_hash_table_remove (priv->focus_siblings, renderer); + else + g_hash_table_insert (priv->focus_siblings, renderer, siblings); +} + +/** + * gtk_cell_area_is_focus_sibling: + * @area: a `GtkCellArea` + * @renderer: the `GtkCellRenderer` expected to have focus + * @sibling: the `GtkCellRenderer` to check against @renderer’s sibling list + * + * Returns whether @sibling is one of @renderer’s focus siblings + * (see gtk_cell_area_add_focus_sibling()). + * + * Returns: %TRUE if @sibling is a focus sibling of @renderer + * + * Deprecated: 4.10 + */ +gboolean +gtk_cell_area_is_focus_sibling (GtkCellArea *area, + GtkCellRenderer *renderer, + GtkCellRenderer *sibling) +{ + GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); + GList *siblings, *l; + + g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE); + g_return_val_if_fail (GTK_IS_CELL_RENDERER (renderer), FALSE); + g_return_val_if_fail (GTK_IS_CELL_RENDERER (sibling), FALSE); + + siblings = g_hash_table_lookup (priv->focus_siblings, renderer); + + for (l = siblings; l; l = l->next) + { + GtkCellRenderer *a_sibling = l->data; + + if (a_sibling == sibling) + return TRUE; + } + + return FALSE; +} + +/** + * gtk_cell_area_get_focus_siblings: + * @area: a `GtkCellArea` + * @renderer: the `GtkCellRenderer` expected to have focus + * + * Gets the focus sibling cell renderers for @renderer. + * + * Returns: (element-type GtkCellRenderer) (transfer none): A `GList` of `GtkCellRenderer`s. + * The returned list is internal and should not be freed. + * + * Deprecated: 4.10 + */ +const GList * +gtk_cell_area_get_focus_siblings (GtkCellArea *area, + GtkCellRenderer *renderer) +{ + GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); + + g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL); + g_return_val_if_fail (GTK_IS_CELL_RENDERER (renderer), NULL); + + return g_hash_table_lookup (priv->focus_siblings, renderer); +} + +/** + * gtk_cell_area_get_focus_from_sibling: + * @area: a `GtkCellArea` + * @renderer: the `GtkCellRenderer` + * + * Gets the `GtkCellRenderer` which is expected to be focusable + * for which @renderer is, or may be a sibling. + * + * This is handy for `GtkCellArea` subclasses when handling events, + * after determining the renderer at the event location it can + * then chose to activate the focus cell for which the event + * cell may have been a sibling. + * + * Returns: (nullable) (transfer none): the `GtkCellRenderer` + * for which @renderer is a sibling + * + * Deprecated: 4.10 + */ +GtkCellRenderer * +gtk_cell_area_get_focus_from_sibling (GtkCellArea *area, + GtkCellRenderer *renderer) +{ + GtkCellRenderer *ret_renderer = NULL; + GList *renderers, *l; + + g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL); + g_return_val_if_fail (GTK_IS_CELL_RENDERER (renderer), NULL); + + renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (area)); + + for (l = renderers; l; l = l->next) + { + GtkCellRenderer *a_renderer = l->data; + const GList *list; + + for (list = gtk_cell_area_get_focus_siblings (area, a_renderer); + list; list = list->next) + { + GtkCellRenderer *sibling_renderer = list->data; + + if (sibling_renderer == renderer) + { + ret_renderer = a_renderer; + break; + } + } + } + g_list_free (renderers); + + return ret_renderer; +} + +/************************************************************* + * API: Cell Activation/Editing * + *************************************************************/ +static void +gtk_cell_area_add_editable (GtkCellArea *area, + GtkCellRenderer *renderer, + GtkCellEditable *editable, + const GdkRectangle *cell_area) +{ + GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); + + g_signal_emit (area, cell_area_signals[SIGNAL_ADD_EDITABLE], 0, + renderer, editable, cell_area, priv->current_path); +} + +static void +gtk_cell_area_remove_editable (GtkCellArea *area, + GtkCellRenderer *renderer, + GtkCellEditable *editable) +{ + g_signal_emit (area, cell_area_signals[SIGNAL_REMOVE_EDITABLE], 0, renderer, editable); +} + +static void +cell_area_remove_widget_cb (GtkCellEditable *editable, + GtkCellArea *area) +{ + GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); + + g_assert (priv->edit_widget == editable); + g_assert (priv->edited_cell != NULL); + + gtk_cell_area_remove_editable (area, priv->edited_cell, priv->edit_widget); + + /* Now that we're done with editing the widget and it can be removed, + * remove our references to the widget and disconnect handlers */ + gtk_cell_area_set_edited_cell (area, NULL); + gtk_cell_area_set_edit_widget (area, NULL); +} + +static void +gtk_cell_area_set_edited_cell (GtkCellArea *area, + GtkCellRenderer *renderer) +{ + GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); + + g_return_if_fail (GTK_IS_CELL_AREA (area)); + g_return_if_fail (renderer == NULL || GTK_IS_CELL_RENDERER (renderer)); + + if (priv->edited_cell != renderer) + { + if (priv->edited_cell) + g_object_unref (priv->edited_cell); + + priv->edited_cell = renderer; + + if (priv->edited_cell) + g_object_ref (priv->edited_cell); + + g_object_notify (G_OBJECT (area), "edited-cell"); + } +} + +static void +gtk_cell_area_set_edit_widget (GtkCellArea *area, + GtkCellEditable *editable) +{ + GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); + + g_return_if_fail (GTK_IS_CELL_AREA (area)); + g_return_if_fail (editable == NULL || GTK_IS_CELL_EDITABLE (editable)); + + if (priv->edit_widget != editable) + { + if (priv->edit_widget) + { + g_signal_handler_disconnect (priv->edit_widget, priv->remove_widget_id); + + g_object_unref (priv->edit_widget); + } + + priv->edit_widget = editable; + + if (priv->edit_widget) + { + priv->remove_widget_id = + g_signal_connect (priv->edit_widget, "remove-widget", + G_CALLBACK (cell_area_remove_widget_cb), area); + + g_object_ref (priv->edit_widget); + } + + g_object_notify (G_OBJECT (area), "edit-widget"); + } +} + +/** + * gtk_cell_area_get_edited_cell: + * @area: a `GtkCellArea` + * + * Gets the `GtkCellRenderer` in @area that is currently + * being edited. + * + * Returns: (transfer none) (nullable): The currently edited `GtkCellRenderer` + * + * Deprecated: 4.10 + */ +GtkCellRenderer * +gtk_cell_area_get_edited_cell (GtkCellArea *area) +{ + GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); + + g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL); + + return priv->edited_cell; +} + +/** + * gtk_cell_area_get_edit_widget: + * @area: a `GtkCellArea` + * + * Gets the `GtkCellEditable` widget currently used + * to edit the currently edited cell. + * + * Returns: (transfer none) (nullable): The currently active `GtkCellEditable` widget + * + * Deprecated: 4.10 + */ +GtkCellEditable * +gtk_cell_area_get_edit_widget (GtkCellArea *area) +{ + GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); + + g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL); + + return priv->edit_widget; +} + +/** + * gtk_cell_area_activate_cell: + * @area: a `GtkCellArea` + * @widget: the `GtkWidget` that @area is rendering onto + * @renderer: the `GtkCellRenderer` in @area to activate + * @event: the `GdkEvent` for which cell activation should occur + * @cell_area: the `GdkRectangle` in @widget relative coordinates + * of @renderer for the current row. + * @flags: the `GtkCellRenderer`State for @renderer + * + * This is used by `GtkCellArea` subclasses when handling events + * to activate cells, the base `GtkCellArea` class activates cells + * for keyboard events for free in its own GtkCellArea->activate() + * implementation. + * + * Returns: whether cell activation was successful + * + * Deprecated: 4.10 + */ +gboolean +gtk_cell_area_activate_cell (GtkCellArea *area, + GtkWidget *widget, + GtkCellRenderer *renderer, + GdkEvent *event, + const GdkRectangle *cell_area, + GtkCellRendererState flags) +{ + GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); + GtkCellRendererMode mode; + + g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE); + g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + g_return_val_if_fail (GTK_IS_CELL_RENDERER (renderer), FALSE); + g_return_val_if_fail (cell_area != NULL, FALSE); + + if (!gtk_cell_renderer_get_sensitive (renderer)) + return FALSE; + + g_object_get (renderer, "mode", &mode, NULL); + + if (mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE) + { + if (gtk_cell_renderer_activate (renderer, + event, widget, + priv->current_path, + cell_area, + cell_area, + flags)) + return TRUE; + } + else if (mode == GTK_CELL_RENDERER_MODE_EDITABLE) + { + GtkCellEditable *editable_widget; + GdkRectangle inner_area; + + gtk_cell_area_inner_cell_area (area, widget, cell_area, &inner_area); + + editable_widget = + gtk_cell_renderer_start_editing (renderer, + event, widget, + priv->current_path, + &inner_area, + &inner_area, + flags); + + if (editable_widget != NULL) + { + g_return_val_if_fail (GTK_IS_CELL_EDITABLE (editable_widget), FALSE); + + gtk_cell_area_set_edited_cell (area, renderer); + gtk_cell_area_set_edit_widget (area, editable_widget); + + /* Signal that editing started so that callers can get + * a handle on the editable_widget */ + gtk_cell_area_add_editable (area, priv->focus_cell, editable_widget, cell_area); + + /* If the signal was successfully handled start the editing */ + if (gtk_widget_get_parent (GTK_WIDGET (editable_widget))) + { + gtk_cell_editable_start_editing (editable_widget, event); + gtk_widget_grab_focus (GTK_WIDGET (editable_widget)); + } + else + { + /* Otherwise clear the editing state and fire a warning */ + gtk_cell_area_set_edited_cell (area, NULL); + gtk_cell_area_set_edit_widget (area, NULL); + + g_warning ("GtkCellArea::add-editable fired in the dark, no cell editing was started."); + } + + return TRUE; + } + } + + return FALSE; +} + +/** + * gtk_cell_area_stop_editing: + * @area: a `GtkCellArea` + * @canceled: whether editing was canceled. + * + * Explicitly stops the editing of the currently edited cell. + * + * If @canceled is %TRUE, the currently edited cell renderer + * will emit the ::editing-canceled signal, otherwise the + * the ::editing-done signal will be emitted on the current + * edit widget. + * + * See gtk_cell_area_get_edited_cell() and gtk_cell_area_get_edit_widget(). + * + * Deprecated: 4.10 + */ +void +gtk_cell_area_stop_editing (GtkCellArea *area, + gboolean canceled) +{ + GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); + + g_return_if_fail (GTK_IS_CELL_AREA (area)); + + if (priv->edited_cell) + { + GtkCellEditable *edit_widget = g_object_ref (priv->edit_widget); + GtkCellRenderer *edit_cell = g_object_ref (priv->edited_cell); + + /* Stop editing of the cell renderer */ + gtk_cell_renderer_stop_editing (priv->edited_cell, canceled); + + /* When editing is explicitly halted either + * the "editing-canceled" signal is emitted on the cell + * renderer or the "editing-done" signal on the GtkCellEditable widget + */ + if (!canceled) + gtk_cell_editable_editing_done (edit_widget); + + /* Remove any references to the editable widget */ + gtk_cell_area_set_edited_cell (area, NULL); + gtk_cell_area_set_edit_widget (area, NULL); + + /* Send the remove-widget signal explicitly (this is done after setting + * the edit cell/widget NULL to avoid feedback) + */ + gtk_cell_area_remove_editable (area, edit_cell, edit_widget); + g_object_unref (edit_cell); + g_object_unref (edit_widget); + } +} + +/************************************************************* + * API: Convenience for area implementations * + *************************************************************/ + +/** + * gtk_cell_area_inner_cell_area: + * @area: a `GtkCellArea` + * @widget: the `GtkWidget` that @area is rendering onto + * @cell_area: the @widget relative coordinates where one of @area’s cells + * is to be placed + * @inner_area: (out): the return location for the inner cell area + * + * This is a convenience function for `GtkCellArea` implementations + * to get the inner area where a given `GtkCellRenderer` will be + * rendered. It removes any padding previously added by gtk_cell_area_request_renderer(). + * + * Deprecated: 4.10 + */ +void +gtk_cell_area_inner_cell_area (GtkCellArea *area, + GtkWidget *widget, + const GdkRectangle *cell_area, + GdkRectangle *inner_area) +{ + GtkBorder border; + GtkStyleContext *context; + + g_return_if_fail (GTK_IS_CELL_AREA (area)); + g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (cell_area != NULL); + g_return_if_fail (inner_area != NULL); + + context = gtk_widget_get_style_context (widget); + gtk_style_context_get_padding (context, &border); + + *inner_area = *cell_area; + + if (border.left + border.right > cell_area->width) + { + border.left = cell_area->width / 2; + border.right = (cell_area->width + 1) / 2; + } + inner_area->x += border.left; + inner_area->width -= border.left + border.right; + if (border.top + border.bottom > cell_area->height) + { + border.top = cell_area->height / 2; + border.bottom = (cell_area->height + 1) / 2; + } + inner_area->y += border.top; + inner_area->height -= border.top + border.bottom; +} + +/** + * gtk_cell_area_request_renderer: + * @area: a `GtkCellArea` + * @renderer: the `GtkCellRenderer` to request size for + * @orientation: the `GtkOrientation` in which to request size + * @widget: the `GtkWidget` that @area is rendering onto + * @for_size: the allocation contextual size to request for, or -1 if + * the base request for the orientation is to be returned. + * @minimum_size: (out) (optional): location to store the minimum size + * @natural_size: (out) (optional): location to store the natural size + * + * This is a convenience function for `GtkCellArea` implementations + * to request size for cell renderers. It’s important to use this + * function to request size and then use gtk_cell_area_inner_cell_area() + * at render and event time since this function will add padding + * around the cell for focus painting. + * + * Deprecated: 4.10 + */ +void +gtk_cell_area_request_renderer (GtkCellArea *area, + GtkCellRenderer *renderer, + GtkOrientation orientation, + GtkWidget *widget, + int for_size, + int *minimum_size, + int *natural_size) +{ + GtkBorder border; + GtkStyleContext *context; + + g_return_if_fail (GTK_IS_CELL_AREA (area)); + g_return_if_fail (GTK_IS_CELL_RENDERER (renderer)); + g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (minimum_size != NULL); + g_return_if_fail (natural_size != NULL); + + context = gtk_widget_get_style_context (widget); + gtk_style_context_get_padding (context, &border); + + if (orientation == GTK_ORIENTATION_HORIZONTAL) + { + if (for_size < 0) + gtk_cell_renderer_get_preferred_width (renderer, widget, minimum_size, natural_size); + else + { + for_size = MAX (0, for_size - border.left - border.right); + + gtk_cell_renderer_get_preferred_width_for_height (renderer, widget, for_size, + minimum_size, natural_size); + } + + *minimum_size += border.left + border.right; + *natural_size += border.left + border.right; + } + else /* GTK_ORIENTATION_VERTICAL */ + { + if (for_size < 0) + gtk_cell_renderer_get_preferred_height (renderer, widget, minimum_size, natural_size); + else + { + for_size = MAX (0, for_size - border.top - border.bottom); + + gtk_cell_renderer_get_preferred_height_for_width (renderer, widget, for_size, + minimum_size, natural_size); + } + + *minimum_size += border.top + border.bottom; + *natural_size += border.top + border.bottom; + } +} + +void +_gtk_cell_area_set_cell_data_func_with_proxy (GtkCellArea *area, + GtkCellRenderer *cell, + GFunc func, + gpointer func_data, + GDestroyNotify destroy, + gpointer proxy) +{ + GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); + CellInfo *info; + + g_return_if_fail (GTK_IS_CELL_AREA (area)); + g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); + + info = g_hash_table_lookup (priv->cell_info, cell); + + /* Note we do not take a reference to the proxy, the proxy is a GtkCellLayout + * that is forwarding its implementation to a delegate GtkCellArea therefore + * its life-cycle is longer than the area's life cycle. + */ + if (info) + { + if (info->destroy && info->data) + info->destroy (info->data); + + if (func) + { + info->func = (GtkCellLayoutDataFunc)func; + info->data = func_data; + info->destroy = destroy; + info->proxy = proxy; + } + else + { + info->func = NULL; + info->data = NULL; + info->destroy = NULL; + info->proxy = NULL; + } + } + else + { + info = cell_info_new ((GtkCellLayoutDataFunc)func, func_data, destroy); + info->proxy = proxy; + + g_hash_table_insert (priv->cell_info, cell, info); + } +} diff --git a/gtk/deprecated/gtkcellarea.h b/gtk/deprecated/gtkcellarea.h new file mode 100644 index 0000000000..9ce52f37b1 --- /dev/null +++ b/gtk/deprecated/gtkcellarea.h @@ -0,0 +1,525 @@ +/* gtkcellarea.h + * + * Copyright (C) 2010 Openismus GmbH + * + * Authors: + * Tristan Van Berkom + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#ifndef __GTK_CELL_AREA_H__ +#define __GTK_CELL_AREA_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_CELL_AREA (gtk_cell_area_get_type ()) +#define GTK_CELL_AREA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_AREA, GtkCellArea)) +#define GTK_CELL_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_CELL_AREA, GtkCellAreaClass)) +#define GTK_IS_CELL_AREA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_AREA)) +#define GTK_IS_CELL_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_CELL_AREA)) +#define GTK_CELL_AREA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CELL_AREA, GtkCellAreaClass)) + +typedef struct _GtkCellArea GtkCellArea; +typedef struct _GtkCellAreaClass GtkCellAreaClass; +typedef struct _GtkCellAreaContext GtkCellAreaContext; + +/** + * GTK_CELL_AREA_WARN_INVALID_CELL_PROPERTY_ID: + * @object: the `GObject` on which set_cell_property() or get_cell_property() + * was called + * @property_id: the numeric id of the property + * @pspec: the `GParamSpec` of the property + * + * This macro should be used to emit a standard warning about unexpected + * properties in set_cell_property() and get_cell_property() implementations. + */ +#define GTK_CELL_AREA_WARN_INVALID_CELL_PROPERTY_ID(object, property_id, pspec) \ + G_OBJECT_WARN_INVALID_PSPEC ((object), "cell property id", (property_id), (pspec)) + +/** + * GtkCellCallback: + * @renderer: the cell renderer to operate on + * @data: (closure): user-supplied data + * + * The type of the callback functions used for iterating over + * the cell renderers of a `GtkCellArea`, see gtk_cell_area_foreach(). + * + * Returns: %TRUE to stop iterating over cells. + */ +typedef gboolean (*GtkCellCallback) (GtkCellRenderer *renderer, + gpointer data); + +/** + * GtkCellAllocCallback: + * @renderer: the cell renderer to operate on + * @cell_area: the area allocated to @renderer inside the rectangle + * provided to gtk_cell_area_foreach_alloc(). + * @cell_background: the background area for @renderer inside the + * background area provided to gtk_cell_area_foreach_alloc(). + * @data: (closure): user-supplied data + * + * The type of the callback functions used for iterating over the + * cell renderers and their allocated areas inside a `GtkCellArea`, + * see gtk_cell_area_foreach_alloc(). + * + * Returns: %TRUE to stop iterating over cells. + */ +typedef gboolean (*GtkCellAllocCallback) (GtkCellRenderer *renderer, + const GdkRectangle *cell_area, + const GdkRectangle *cell_background, + gpointer data); + + +struct _GtkCellArea +{ + /*< private >*/ + GInitiallyUnowned parent_instance; +}; + + +/** + * GtkCellAreaClass: + * @add: adds a `GtkCellRenderer` to the area. + * @remove: removes a `GtkCellRenderer` from the area. + * @foreach: calls the `GtkCellCallback` function on every `GtkCellRenderer` in + * the area with the provided user data until the callback returns %TRUE. + * @foreach_alloc: Calls the `GtkCellAllocCallback` function on every + * `GtkCellRenderer` in the area with the allocated area for the cell + * and the provided user data until the callback returns %TRUE. + * @event: Handle an event in the area, this is generally used to activate + * a cell at the event location for button events but can also be used + * to generically pass events to `GtkWidget`s drawn onto the area. + * @snapshot: Actually snapshot the area’s cells to the specified rectangle, + * @background_area should be correctly distributed to the cells + * corresponding background areas. + * @apply_attributes: Apply the cell attributes to the cells. This is + * implemented as a signal and generally `GtkCellArea` subclasses don't + * need to implement it since it is handled by the base class. + * @create_context: Creates and returns a class specific `GtkCellAreaContext` + * to store cell alignment and allocation details for a said `GtkCellArea` + * class. + * @copy_context: Creates a new `GtkCellAreaContext` in the same state as + * the passed @context with any cell alignment data and allocations intact. + * @get_request_mode: This allows an area to tell its layouting widget whether + * it prefers to be allocated in %GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH or + * %GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT mode. + * @get_preferred_width: Calculates the minimum and natural width of the + * areas cells with the current attributes applied while considering + * the particular layouting details of the said `GtkCellArea`. While + * requests are performed over a series of rows, alignments and overall + * minimum and natural sizes should be stored in the corresponding + * `GtkCellAreaContext`. + * @get_preferred_height_for_width: Calculates the minimum and natural height + * for the area if the passed @context would be allocated the given width. + * When implementing this virtual method it is safe to assume that @context + * has already stored the aligned cell widths for every `GtkTreeModel` row + * that @context will be allocated for since this information was stored + * at `GtkCellAreaClass.get_preferred_width()` time. This virtual method + * should also store any necessary alignments of cell heights for the + * case that the context is allocated a height. + * @get_preferred_height: Calculates the minimum and natural height of the + * areas cells with the current attributes applied. Essentially this is + * the same as `GtkCellAreaClass.get_preferred_width()` only for areas + * that are being requested as %GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT. + * @get_preferred_width_for_height: Calculates the minimum and natural width + * for the area if the passed @context would be allocated the given + * height. The same as `GtkCellAreaClass.get_preferred_height_for_width()` + * only for handling requests in the %GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT + * mode. + * @set_cell_property: This should be implemented to handle changes in child + * cell properties for a given `GtkCellRenderer` that were previously + * installed on the `GtkCellAreaClass` with gtk_cell_area_class_install_cell_property(). + * @get_cell_property: This should be implemented to report the values of + * child cell properties for a given child `GtkCellRenderer`. + * @focus: This virtual method should be implemented to navigate focus from + * cell to cell inside the `GtkCellArea`. The `GtkCellArea` should move + * focus from cell to cell inside the area and return %FALSE if focus + * logically leaves the area with the following exceptions: When the + * area contains no activatable cells, the entire area receives focus. + * Focus should not be given to cells that are actually “focus siblings” + * of other sibling cells (see gtk_cell_area_get_focus_from_sibling()). + * Focus is set by calling gtk_cell_area_set_focus_cell(). + * @is_activatable: Returns whether the `GtkCellArea` can respond to + * `GtkCellAreaClass.activate()`, usually this does not need to be + * implemented since the base class takes care of this however it can + * be enhanced if the `GtkCellArea` subclass can handle activation in + * other ways than activating its `GtkCellRenderers`. + * @activate: This is called when the layouting widget rendering the + * `GtkCellArea` activates the focus cell (see gtk_cell_area_get_focus_cell()). + */ +struct _GtkCellAreaClass +{ + /*< private >*/ + GInitiallyUnownedClass parent_class; + + /*< public >*/ + + /* Basic methods */ + void (* add) (GtkCellArea *area, + GtkCellRenderer *renderer); + void (* remove) (GtkCellArea *area, + GtkCellRenderer *renderer); + void (* foreach) (GtkCellArea *area, + GtkCellCallback callback, + gpointer callback_data); + void (* foreach_alloc) (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + const GdkRectangle *cell_area, + const GdkRectangle *background_area, + GtkCellAllocCallback callback, + gpointer callback_data); + int (* event) (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + GdkEvent *event, + const GdkRectangle *cell_area, + GtkCellRendererState flags); + void (* snapshot) (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + GtkSnapshot *snapshot, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + GtkCellRendererState flags, + gboolean paint_focus); + void (* apply_attributes) (GtkCellArea *area, + GtkTreeModel *tree_model, + GtkTreeIter *iter, + gboolean is_expander, + gboolean is_expanded); + + /* Geometry */ + GtkCellAreaContext *(* create_context) (GtkCellArea *area); + GtkCellAreaContext *(* copy_context) (GtkCellArea *area, + GtkCellAreaContext *context); + GtkSizeRequestMode (* get_request_mode) (GtkCellArea *area); + void (* get_preferred_width) (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + int *minimum_width, + int *natural_width); + void (* get_preferred_height_for_width) (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + int width, + int *minimum_height, + int *natural_height); + void (* get_preferred_height) (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + int *minimum_height, + int *natural_height); + void (* get_preferred_width_for_height) (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + int height, + int *minimum_width, + int *natural_width); + + /* Cell Properties */ + void (* set_cell_property) (GtkCellArea *area, + GtkCellRenderer *renderer, + guint property_id, + const GValue *value, + GParamSpec *pspec); + void (* get_cell_property) (GtkCellArea *area, + GtkCellRenderer *renderer, + guint property_id, + GValue *value, + GParamSpec *pspec); + + /* Focus */ + gboolean (* focus) (GtkCellArea *area, + GtkDirectionType direction); + gboolean (* is_activatable) (GtkCellArea *area); + gboolean (* activate) (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + const GdkRectangle *cell_area, + GtkCellRendererState flags, + gboolean edit_only); + + /*< private >*/ + + gpointer padding[8]; +}; + +GDK_AVAILABLE_IN_ALL +GType gtk_cell_area_get_type (void) G_GNUC_CONST; + +/* Basic methods */ +GDK_DEPRECATED_IN_4_10 +void gtk_cell_area_add (GtkCellArea *area, + GtkCellRenderer *renderer); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_area_remove (GtkCellArea *area, + GtkCellRenderer *renderer); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_cell_area_has_renderer (GtkCellArea *area, + GtkCellRenderer *renderer); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_area_foreach (GtkCellArea *area, + GtkCellCallback callback, + gpointer callback_data); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_area_foreach_alloc (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + const GdkRectangle *cell_area, + const GdkRectangle *background_area, + GtkCellAllocCallback callback, + gpointer callback_data); +GDK_DEPRECATED_IN_4_10 +int gtk_cell_area_event (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + GdkEvent *event, + const GdkRectangle *cell_area, + GtkCellRendererState flags); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_area_snapshot (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + GtkSnapshot *snapshot, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + GtkCellRendererState flags, + gboolean paint_focus); + +GDK_DEPRECATED_IN_4_10 +void gtk_cell_area_get_cell_allocation (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + GtkCellRenderer *renderer, + const GdkRectangle *cell_area, + GdkRectangle *allocation); +GDK_DEPRECATED_IN_4_10 +GtkCellRenderer *gtk_cell_area_get_cell_at_position (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + const GdkRectangle *cell_area, + int x, + int y, + GdkRectangle *alloc_area); + +/* Geometry */ +GDK_DEPRECATED_IN_4_10 +GtkCellAreaContext *gtk_cell_area_create_context (GtkCellArea *area); +GDK_DEPRECATED_IN_4_10 +GtkCellAreaContext *gtk_cell_area_copy_context (GtkCellArea *area, + GtkCellAreaContext *context); +GDK_DEPRECATED_IN_4_10 +GtkSizeRequestMode gtk_cell_area_get_request_mode (GtkCellArea *area); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_area_get_preferred_width (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + int *minimum_width, + int *natural_width); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_area_get_preferred_height_for_width (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + int width, + int *minimum_height, + int *natural_height); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_area_get_preferred_height (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + int *minimum_height, + int *natural_height); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_area_get_preferred_width_for_height (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + int height, + int *minimum_width, + int *natural_width); +GDK_DEPRECATED_IN_4_10 +const char * gtk_cell_area_get_current_path_string (GtkCellArea *area); + + +/* Attributes */ +GDK_DEPRECATED_IN_4_10 +void gtk_cell_area_apply_attributes (GtkCellArea *area, + GtkTreeModel *tree_model, + GtkTreeIter *iter, + gboolean is_expander, + gboolean is_expanded); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_area_attribute_connect (GtkCellArea *area, + GtkCellRenderer *renderer, + const char *attribute, + int column); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_area_attribute_disconnect (GtkCellArea *area, + GtkCellRenderer *renderer, + const char *attribute); +GDK_DEPRECATED_IN_4_10 +int gtk_cell_area_attribute_get_column (GtkCellArea *area, + GtkCellRenderer *renderer, + const char *attribute); + + +/* Cell Properties */ +GDK_DEPRECATED_IN_4_10 +void gtk_cell_area_class_install_cell_property (GtkCellAreaClass *aclass, + guint property_id, + GParamSpec *pspec); +GDK_DEPRECATED_IN_4_10 +GParamSpec* gtk_cell_area_class_find_cell_property (GtkCellAreaClass *aclass, + const char *property_name); +GDK_DEPRECATED_IN_4_10 +GParamSpec** gtk_cell_area_class_list_cell_properties (GtkCellAreaClass *aclass, + guint *n_properties); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_area_add_with_properties (GtkCellArea *area, + GtkCellRenderer *renderer, + const char *first_prop_name, + ...) G_GNUC_NULL_TERMINATED; +GDK_DEPRECATED_IN_4_10 +void gtk_cell_area_cell_set (GtkCellArea *area, + GtkCellRenderer *renderer, + const char *first_prop_name, + ...) G_GNUC_NULL_TERMINATED; +GDK_DEPRECATED_IN_4_10 +void gtk_cell_area_cell_get (GtkCellArea *area, + GtkCellRenderer *renderer, + const char *first_prop_name, + ...) G_GNUC_NULL_TERMINATED; +GDK_DEPRECATED_IN_4_10 +void gtk_cell_area_cell_set_valist (GtkCellArea *area, + GtkCellRenderer *renderer, + const char *first_property_name, + va_list var_args); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_area_cell_get_valist (GtkCellArea *area, + GtkCellRenderer *renderer, + const char *first_property_name, + va_list var_args); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_area_cell_set_property (GtkCellArea *area, + GtkCellRenderer *renderer, + const char *property_name, + const GValue *value); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_area_cell_get_property (GtkCellArea *area, + GtkCellRenderer *renderer, + const char *property_name, + GValue *value); + +/* Focus */ +GDK_DEPRECATED_IN_4_10 +gboolean gtk_cell_area_is_activatable (GtkCellArea *area); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_cell_area_activate (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + const GdkRectangle *cell_area, + GtkCellRendererState flags, + gboolean edit_only); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_cell_area_focus (GtkCellArea *area, + GtkDirectionType direction); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_area_set_focus_cell (GtkCellArea *area, + GtkCellRenderer *renderer); +GDK_DEPRECATED_IN_4_10 +GtkCellRenderer *gtk_cell_area_get_focus_cell (GtkCellArea *area); + + +/* Focus siblings */ +GDK_DEPRECATED_IN_4_10 +void gtk_cell_area_add_focus_sibling (GtkCellArea *area, + GtkCellRenderer *renderer, + GtkCellRenderer *sibling); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_area_remove_focus_sibling (GtkCellArea *area, + GtkCellRenderer *renderer, + GtkCellRenderer *sibling); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_cell_area_is_focus_sibling (GtkCellArea *area, + GtkCellRenderer *renderer, + GtkCellRenderer *sibling); +GDK_DEPRECATED_IN_4_10 +const GList * gtk_cell_area_get_focus_siblings (GtkCellArea *area, + GtkCellRenderer *renderer); +GDK_DEPRECATED_IN_4_10 +GtkCellRenderer *gtk_cell_area_get_focus_from_sibling (GtkCellArea *area, + GtkCellRenderer *renderer); + +/* Cell Activation/Editing */ +GDK_DEPRECATED_IN_4_10 +GtkCellRenderer *gtk_cell_area_get_edited_cell (GtkCellArea *area); +GDK_DEPRECATED_IN_4_10 +GtkCellEditable *gtk_cell_area_get_edit_widget (GtkCellArea *area); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_cell_area_activate_cell (GtkCellArea *area, + GtkWidget *widget, + GtkCellRenderer *renderer, + GdkEvent *event, + const GdkRectangle *cell_area, + GtkCellRendererState flags); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_area_stop_editing (GtkCellArea *area, + gboolean canceled); + +/* Functions for area implementations */ + +/* Distinguish the inner cell area from the whole requested area including margins */ +GDK_DEPRECATED_IN_4_10 +void gtk_cell_area_inner_cell_area (GtkCellArea *area, + GtkWidget *widget, + const GdkRectangle *cell_area, + GdkRectangle *inner_area); + +/* Request the size of a cell while respecting the cell margins (requests are margin inclusive) */ +GDK_DEPRECATED_IN_4_10 +void gtk_cell_area_request_renderer (GtkCellArea *area, + GtkCellRenderer *renderer, + GtkOrientation orientation, + GtkWidget *widget, + int for_size, + int *minimum_size, + int *natural_size); + +/* For api stability, this is called from gtkcelllayout.c in order to ensure the correct + * object is passed to the user function in gtk_cell_layout_set_cell_data_func. + * + * This private api takes gpointer & GFunc arguments to circumvent circular header file + * dependencies. + */ +void _gtk_cell_area_set_cell_data_func_with_proxy (GtkCellArea *area, + GtkCellRenderer *cell, + GFunc func, + gpointer func_data, + GDestroyNotify destroy, + gpointer proxy); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkCellArea, g_object_unref) + +G_END_DECLS + +#endif /* __GTK_CELL_AREA_H__ */ diff --git a/gtk/deprecated/gtkcellareabox.c b/gtk/deprecated/gtkcellareabox.c new file mode 100644 index 0000000000..4ac2f80a53 --- /dev/null +++ b/gtk/deprecated/gtkcellareabox.c @@ -0,0 +1,2248 @@ +/* gtkcellareabox.c + * + * Copyright (C) 2010 Openismus GmbH + * + * Authors: + * Tristan Van Berkom + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + + +/** + * GtkCellAreaBox: + * + * A cell area that renders GtkCellRenderers into a row or a column + * + * The `GtkCellAreaBox` renders cell renderers into a row or a column + * depending on its `GtkOrientation`. + * + * GtkCellAreaBox uses a notion of packing. Packing + * refers to adding cell renderers with reference to a particular position + * in a `GtkCellAreaBox`. There are two reference positions: the + * start and the end of the box. + * When the `GtkCellAreaBox` is oriented in the %GTK_ORIENTATION_VERTICAL + * orientation, the start is defined as the top of the box and the end is + * defined as the bottom. In the %GTK_ORIENTATION_HORIZONTAL orientation + * start is defined as the left side and the end is defined as the right + * side. + * + * Alignments of `GtkCellRenderer`s rendered in adjacent rows can be + * configured by configuring the `GtkCellAreaBox` align child cell property + * with gtk_cell_area_cell_set_property() or by specifying the "align" + * argument to gtk_cell_area_box_pack_start() and gtk_cell_area_box_pack_end(). + */ + +#include "config.h" +#include "gtkorientable.h" +#include "deprecated/gtkcelllayout.h" +#include "gtkcellareabox.h" +#include "gtkcellareaboxcontextprivate.h" +#include "gtktypebuiltins.h" +#include "gtkprivate.h" + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + +/* GObjectClass */ +static void gtk_cell_area_box_finalize (GObject *object); +static void gtk_cell_area_box_dispose (GObject *object); +static void gtk_cell_area_box_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gtk_cell_area_box_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); + +/* GtkCellAreaClass */ +static void gtk_cell_area_box_add (GtkCellArea *area, + GtkCellRenderer *renderer); +static void gtk_cell_area_box_remove (GtkCellArea *area, + GtkCellRenderer *renderer); +static void gtk_cell_area_box_foreach (GtkCellArea *area, + GtkCellCallback callback, + gpointer callback_data); +static void gtk_cell_area_box_foreach_alloc (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + const GdkRectangle *cell_area, + const GdkRectangle *background_area, + GtkCellAllocCallback callback, + gpointer callback_data); +static void gtk_cell_area_box_apply_attributes (GtkCellArea *area, + GtkTreeModel *tree_model, + GtkTreeIter *iter, + gboolean is_expander, + gboolean is_expanded); +static void gtk_cell_area_box_set_cell_property (GtkCellArea *area, + GtkCellRenderer *renderer, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gtk_cell_area_box_get_cell_property (GtkCellArea *area, + GtkCellRenderer *renderer, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static GtkCellAreaContext *gtk_cell_area_box_create_context (GtkCellArea *area); +static GtkCellAreaContext *gtk_cell_area_box_copy_context (GtkCellArea *area, + GtkCellAreaContext *context); +static GtkSizeRequestMode gtk_cell_area_box_get_request_mode (GtkCellArea *area); +static void gtk_cell_area_box_get_preferred_width (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + int *minimum_width, + int *natural_width); +static void gtk_cell_area_box_get_preferred_height (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + int *minimum_height, + int *natural_height); +static void gtk_cell_area_box_get_preferred_height_for_width (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + int width, + int *minimum_height, + int *natural_height); +static void gtk_cell_area_box_get_preferred_width_for_height (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + int height, + int *minimum_width, + int *natural_width); +static gboolean gtk_cell_area_box_focus (GtkCellArea *area, + GtkDirectionType direction); + +/* GtkCellLayoutIface */ +static void gtk_cell_area_box_cell_layout_init (GtkCellLayoutIface *iface); +static void gtk_cell_area_box_layout_pack_start (GtkCellLayout *cell_layout, + GtkCellRenderer *renderer, + gboolean expand); +static void gtk_cell_area_box_layout_pack_end (GtkCellLayout *cell_layout, + GtkCellRenderer *renderer, + gboolean expand); +static void gtk_cell_area_box_layout_reorder (GtkCellLayout *cell_layout, + GtkCellRenderer *renderer, + int position); +static void gtk_cell_area_box_focus_changed (GtkCellArea *area, + GParamSpec *pspec, + GtkCellAreaBox *box); + + +/* CellInfo/CellGroup metadata handling and convenience functions */ +typedef struct { + GtkCellRenderer *renderer; + + guint expand : 1; /* Whether the cell expands */ + guint pack : 1; /* Whether it is packed from the start or end */ + guint align : 1; /* Whether to align its position with adjacent rows */ + guint fixed : 1; /* Whether to require the same size for all rows */ +} CellInfo; + +typedef struct { + GList *cells; + + guint id : 8; + guint n_cells : 8; + guint expand_cells : 8; + guint align : 1; + guint visible : 1; +} CellGroup; + +typedef struct { + GtkCellRenderer *renderer; + + int position; + int size; +} AllocatedCell; + +static CellInfo *cell_info_new (GtkCellRenderer *renderer, + GtkPackType pack, + gboolean expand, + gboolean align, + gboolean fixed); +static void cell_info_free (CellInfo *info); +static int cell_info_find (CellInfo *info, + GtkCellRenderer *renderer); + +static AllocatedCell *allocated_cell_new (GtkCellRenderer *renderer, + int position, + int size); +static void allocated_cell_free (AllocatedCell *cell); +static GList *list_consecutive_cells (GtkCellAreaBox *box); +static int count_expand_groups (GtkCellAreaBox *box); +static void context_weak_notify (GtkCellAreaBox *box, + GtkCellAreaBoxContext *dead_context); +static void reset_contexts (GtkCellAreaBox *box); +static void init_context_groups (GtkCellAreaBox *box); +static void init_context_group (GtkCellAreaBox *box, + GtkCellAreaBoxContext *context); +static GSList *get_allocated_cells (GtkCellAreaBox *box, + GtkCellAreaBoxContext *context, + GtkWidget *widget, + int width, + int height); + +typedef struct _GtkCellAreaBoxClass GtkCellAreaBoxClass; +typedef struct _GtkCellAreaBoxPrivate GtkCellAreaBoxPrivate; + +struct _GtkCellAreaBox +{ + GtkCellArea parent_instance; +}; + +struct _GtkCellAreaBoxClass +{ + GtkCellAreaClass parent_class; +}; + +struct _GtkCellAreaBoxPrivate +{ + /* We hold on to the previously focused cell when navigating + * up and down in a horizontal box (or left and right on a vertical one) + * this way we always re-enter the last focused cell. + */ + GtkCellRenderer *last_focus_cell; + gulong focus_cell_id; + + GList *cells; + GArray *groups; + + GSList *contexts; + + GtkOrientation orientation; + int spacing; + + /* We hold on to the rtl state from a widget we are requested for + * so that we can navigate focus correctly + */ + gboolean rtl; +}; + +enum { + PROP_0, + PROP_ORIENTATION, + PROP_SPACING +}; + +enum { + CELL_PROP_0, + CELL_PROP_EXPAND, + CELL_PROP_ALIGN, + CELL_PROP_FIXED_SIZE, + CELL_PROP_PACK_TYPE +}; + +G_DEFINE_TYPE_WITH_CODE (GtkCellAreaBox, gtk_cell_area_box, GTK_TYPE_CELL_AREA, + G_ADD_PRIVATE (GtkCellAreaBox) + G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT, + gtk_cell_area_box_cell_layout_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL)) + +static void +gtk_cell_area_box_init (GtkCellAreaBox *box) +{ + GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); + + priv->orientation = GTK_ORIENTATION_HORIZONTAL; + priv->groups = g_array_new (FALSE, TRUE, sizeof (CellGroup)); + priv->cells = NULL; + priv->contexts = NULL; + priv->spacing = 0; + priv->rtl = FALSE; + + /* Watch whenever focus is given to a cell, even if it's not with keynav, + * this way we remember upon entry of the area where focus was last time + * around + */ + priv->focus_cell_id = g_signal_connect (box, "notify::focus-cell", + G_CALLBACK (gtk_cell_area_box_focus_changed), box); +} + +static void +gtk_cell_area_box_class_init (GtkCellAreaBoxClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + GtkCellAreaClass *area_class = GTK_CELL_AREA_CLASS (class); + + /* GObjectClass */ + object_class->finalize = gtk_cell_area_box_finalize; + object_class->dispose = gtk_cell_area_box_dispose; + object_class->set_property = gtk_cell_area_box_set_property; + object_class->get_property = gtk_cell_area_box_get_property; + + /* GtkCellAreaClass */ + area_class->add = gtk_cell_area_box_add; + area_class->remove = gtk_cell_area_box_remove; + area_class->foreach = gtk_cell_area_box_foreach; + area_class->foreach_alloc = gtk_cell_area_box_foreach_alloc; + area_class->apply_attributes = gtk_cell_area_box_apply_attributes; + area_class->set_cell_property = gtk_cell_area_box_set_cell_property; + area_class->get_cell_property = gtk_cell_area_box_get_cell_property; + + area_class->create_context = gtk_cell_area_box_create_context; + area_class->copy_context = gtk_cell_area_box_copy_context; + area_class->get_request_mode = gtk_cell_area_box_get_request_mode; + area_class->get_preferred_width = gtk_cell_area_box_get_preferred_width; + area_class->get_preferred_height = gtk_cell_area_box_get_preferred_height; + area_class->get_preferred_height_for_width = gtk_cell_area_box_get_preferred_height_for_width; + area_class->get_preferred_width_for_height = gtk_cell_area_box_get_preferred_width_for_height; + + area_class->focus = gtk_cell_area_box_focus; + + /* Properties */ + g_object_class_override_property (object_class, PROP_ORIENTATION, "orientation"); + + /** + * GtkCellAreaBox:spacing: + * + * The amount of space to reserve between cells. + */ + g_object_class_install_property (object_class, + PROP_SPACING, + g_param_spec_int ("spacing", NULL, NULL, + 0, + G_MAXINT, + 0, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + /* Cell Properties */ + /** + * GtkCellAreaBox:expand: + * + * Whether the cell renderer should receive extra space + * when the area receives more than its natural size. + */ + gtk_cell_area_class_install_cell_property (area_class, + CELL_PROP_EXPAND, + g_param_spec_boolean + ("expand", NULL, NULL, + FALSE, + GTK_PARAM_READWRITE)); + + /** + * GtkCellAreaBox:align: + * + * Whether the cell renderer should be aligned in adjacent rows. + */ + gtk_cell_area_class_install_cell_property (area_class, + CELL_PROP_ALIGN, + g_param_spec_boolean + ("align", NULL, NULL, + FALSE, + GTK_PARAM_READWRITE)); + + /** + * GtkCellAreaBox:fixed-size: + * + * Whether the cell renderer should require the same size + * for all rows for which it was requested. + */ + gtk_cell_area_class_install_cell_property (area_class, + CELL_PROP_FIXED_SIZE, + g_param_spec_boolean + ("fixed-size", NULL, NULL, + TRUE, + GTK_PARAM_READWRITE)); + + /** + * GtkCellAreaBox:pack-type: + * + * A GtkPackType indicating whether the cell renderer is packed + * with reference to the start or end of the area. + */ + gtk_cell_area_class_install_cell_property (area_class, + CELL_PROP_PACK_TYPE, + g_param_spec_enum + ("pack-type", NULL, NULL, + GTK_TYPE_PACK_TYPE, GTK_PACK_START, + GTK_PARAM_READWRITE)); +} + + +/************************************************************* + * CellInfo/CellGroup basics and convenience functions * + *************************************************************/ +static CellInfo * +cell_info_new (GtkCellRenderer *renderer, + GtkPackType pack, + gboolean expand, + gboolean align, + gboolean fixed) +{ + CellInfo *info = g_slice_new (CellInfo); + + info->renderer = g_object_ref_sink (renderer); + info->pack = pack; + info->expand = expand; + info->align = align; + info->fixed = fixed; + + return info; +} + +static void +cell_info_free (CellInfo *info) +{ + g_object_unref (info->renderer); + + g_slice_free (CellInfo, info); +} + +static int +cell_info_find (CellInfo *info, + GtkCellRenderer *renderer) +{ + return (info->renderer == renderer) ? 0 : -1; +} + +static AllocatedCell * +allocated_cell_new (GtkCellRenderer *renderer, + int position, + int size) +{ + AllocatedCell *cell = g_slice_new (AllocatedCell); + + cell->renderer = renderer; + cell->position = position; + cell->size = size; + + return cell; +} + +static void +allocated_cell_free (AllocatedCell *cell) +{ + g_slice_free (AllocatedCell, cell); +} + +static GList * +list_consecutive_cells (GtkCellAreaBox *box) +{ + GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); + GList *l, *consecutive_cells = NULL, *pack_end_cells = NULL; + CellInfo *info; + + /* List cells in consecutive order taking their + * PACK_START/PACK_END options into account + */ + for (l = priv->cells; l; l = l->next) + { + info = l->data; + + if (info->pack == GTK_PACK_START) + consecutive_cells = g_list_prepend (consecutive_cells, info); + } + + for (l = priv->cells; l; l = l->next) + { + info = l->data; + + if (info->pack == GTK_PACK_END) + pack_end_cells = g_list_prepend (pack_end_cells, info); + } + + consecutive_cells = g_list_reverse (consecutive_cells); + consecutive_cells = g_list_concat (consecutive_cells, pack_end_cells); + + return consecutive_cells; +} + +static void +cell_groups_clear (GtkCellAreaBox *box) +{ + GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); + int i; + + for (i = 0; i < priv->groups->len; i++) + { + CellGroup *group = &g_array_index (priv->groups, CellGroup, i); + + g_list_free (group->cells); + } + + g_array_set_size (priv->groups, 0); +} + +static void +cell_groups_rebuild (GtkCellAreaBox *box) +{ + GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); + CellGroup group = { 0, }; + CellGroup *group_ptr; + GList *cells, *l; + guint id = 0; + gboolean last_cell_fixed = FALSE; + + cell_groups_clear (box); + + if (!priv->cells) + return; + + cells = list_consecutive_cells (box); + + /* First group is implied */ + g_array_append_val (priv->groups, group); + group_ptr = &g_array_index (priv->groups, CellGroup, id); + + for (l = cells; l; l = l->next) + { + CellInfo *info = l->data; + + /* A new group starts with any aligned cell, or + * at the beginning and end of a fixed size cell. + * the first group is implied */ + if ((info->align || info->fixed || last_cell_fixed) && l != cells) + { + memset (&group, 0x0, sizeof (CellGroup)); + group.id = ++id; + + g_array_append_val (priv->groups, group); + group_ptr = &g_array_index (priv->groups, CellGroup, id); + } + + group_ptr->cells = g_list_prepend (group_ptr->cells, info); + group_ptr->n_cells++; + + /* Not every group is aligned, some are floating + * fixed size cells */ + if (info->align) + group_ptr->align = TRUE; + + /* A group expands if it contains any expand cells */ + if (info->expand) + group_ptr->expand_cells++; + + last_cell_fixed = info->fixed; + } + + g_list_free (cells); + + for (id = 0; id < priv->groups->len; id++) + { + group_ptr = &g_array_index (priv->groups, CellGroup, id); + + group_ptr->cells = g_list_reverse (group_ptr->cells); + } + + /* Contexts need to be updated with the new grouping information */ + init_context_groups (box); +} + +static int +count_visible_cells (CellGroup *group, + int *expand_cells) +{ + GList *l; + int visible_cells = 0; + int n_expand_cells = 0; + + for (l = group->cells; l; l = l->next) + { + CellInfo *info = l->data; + + if (gtk_cell_renderer_get_visible (info->renderer)) + { + visible_cells++; + + if (info->expand) + n_expand_cells++; + } + } + + if (expand_cells) + *expand_cells = n_expand_cells; + + return visible_cells; +} + +static int +count_expand_groups (GtkCellAreaBox *box) +{ + GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); + int i; + int expand_groups = 0; + + for (i = 0; i < priv->groups->len; i++) + { + CellGroup *group = &g_array_index (priv->groups, CellGroup, i); + + if (group->expand_cells > 0) + expand_groups++; + } + + return expand_groups; +} + +static void +context_weak_notify (GtkCellAreaBox *box, + GtkCellAreaBoxContext *dead_context) +{ + GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); + + priv->contexts = g_slist_remove (priv->contexts, dead_context); +} + +static void +init_context_group (GtkCellAreaBox *box, + GtkCellAreaBoxContext *context) +{ + GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); + int *expand_groups, *align_groups, i; + + expand_groups = g_new (gboolean, priv->groups->len); + align_groups = g_new (gboolean, priv->groups->len); + + for (i = 0; i < priv->groups->len; i++) + { + CellGroup *group = &g_array_index (priv->groups, CellGroup, i); + + expand_groups[i] = (group->expand_cells > 0); + align_groups[i] = group->align; + } + + /* This call implies resetting the request info */ + _gtk_cell_area_box_init_groups (context, priv->groups->len, expand_groups, align_groups); + g_free (expand_groups); + g_free (align_groups); +} + +static void +init_context_groups (GtkCellAreaBox *box) +{ + GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); + GSList *l; + + /* When the box's groups are reconstructed, + * contexts need to be reinitialized. + */ + for (l = priv->contexts; l; l = l->next) + { + GtkCellAreaBoxContext *context = l->data; + + init_context_group (box, context); + } +} + +static void +reset_contexts (GtkCellAreaBox *box) +{ + GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); + GSList *l; + + /* When the box layout changes, contexts need to + * be reset and sizes for the box get requested again + */ + for (l = priv->contexts; l; l = l->next) + { + GtkCellAreaContext *context = l->data; + + gtk_cell_area_context_reset (context); + } +} + +/* Fall back on a completely unaligned dynamic allocation of cells + * when not allocated for the said orientation, alignment of cells + * is not done when each area gets a different size in the orientation + * of the box. + */ +static GSList * +allocate_cells_manually (GtkCellAreaBox *box, + GtkWidget *widget, + int width, + int height) +{ + GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); + GList *cells, *l; + GSList *allocated_cells = NULL; + GtkRequestedSize *sizes; + int i; + int nvisible = 0, nexpand = 0, group_expand; + int avail_size, extra_size, extra_extra, full_size; + int position = 0, for_size; + gboolean rtl; + + if (!priv->cells) + return NULL; + + /* For vertical oriented boxes, we just let the cell renderers + * realign themselves for rtl + */ + rtl = (priv->orientation == GTK_ORIENTATION_HORIZONTAL && + gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL); + + cells = list_consecutive_cells (box); + + /* Count the visible and expand cells */ + for (i = 0; i < priv->groups->len; i++) + { + CellGroup *group = &g_array_index (priv->groups, CellGroup, i); + + nvisible += count_visible_cells (group, &group_expand); + nexpand += group_expand; + } + + if (nvisible <= 0) + { + g_list_free (cells); + return NULL; + } + + if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) + { + full_size = avail_size = width; + for_size = height; + } + else + { + full_size = avail_size = height; + for_size = width; + } + + /* Go ahead and collect the requests on the fly */ + sizes = g_new0 (GtkRequestedSize, nvisible); + for (l = cells, i = 0; l; l = l->next) + { + CellInfo *info = l->data; + + if (!gtk_cell_renderer_get_visible (info->renderer)) + continue; + + gtk_cell_area_request_renderer (GTK_CELL_AREA (box), info->renderer, + priv->orientation, + widget, for_size, + &sizes[i].minimum_size, + &sizes[i].natural_size); + + avail_size -= sizes[i].minimum_size; + + sizes[i].data = info; + + i++; + } + + /* Naturally distribute the allocation */ + avail_size -= (nvisible - 1) * priv->spacing; + if (avail_size > 0) + avail_size = gtk_distribute_natural_allocation (avail_size, nvisible, sizes); + else + avail_size = 0; + + /* Calculate/distribute expand for cells */ + if (nexpand > 0) + { + extra_size = avail_size / nexpand; + extra_extra = avail_size % nexpand; + } + else + extra_size = extra_extra = 0; + + /* Create the allocated cells */ + for (i = 0; i < nvisible; i++) + { + CellInfo *info = sizes[i].data; + AllocatedCell *cell; + + if (info->expand) + { + sizes[i].minimum_size += extra_size; + if (extra_extra) + { + sizes[i].minimum_size++; + extra_extra--; + } + } + + if (rtl) + cell = allocated_cell_new (info->renderer, + full_size - (position + sizes[i].minimum_size), + sizes[i].minimum_size); + else + cell = allocated_cell_new (info->renderer, position, sizes[i].minimum_size); + + allocated_cells = g_slist_prepend (allocated_cells, cell); + + position += sizes[i].minimum_size; + position += priv->spacing; + } + + g_free (sizes); + g_list_free (cells); + + /* Note it might not be important to reverse the list here at all, + * we have the correct positions, no need to allocate from left to right + */ + return g_slist_reverse (allocated_cells); +} + +/* Returns an allocation for each cell in the orientation of the box, + * used in ->render()/->event() implementations to get a straight-forward + * list of allocated cells to operate on. + */ +static GSList * +get_allocated_cells (GtkCellAreaBox *box, + GtkCellAreaBoxContext *context, + GtkWidget *widget, + int width, + int height) +{ + GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); + GtkCellAreaBoxAllocation *group_allocs; + GtkCellArea *area = GTK_CELL_AREA (box); + GList *cell_list; + GSList *allocated_cells = NULL; + int i, j, n_allocs, position; + int for_size, full_size; + gboolean rtl; + + group_allocs = _gtk_cell_area_box_context_get_orientation_allocs (context, &n_allocs); + if (!group_allocs) + return allocate_cells_manually (box, widget, width, height); + + if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) + { + full_size = width; + for_size = height; + } + else + { + full_size = height; + for_size = width; + } + + /* For vertical oriented boxes, we just let the cell renderers + * realign themselves for rtl + */ + rtl = (priv->orientation == GTK_ORIENTATION_HORIZONTAL && + gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL); + + for (position = 0, i = 0; i < n_allocs; i++) + { + /* We dont always allocate all groups, sometimes the requested + * group has only invisible cells for every row, hence the usage + * of group_allocs[i].group_idx here + */ + CellGroup *group = &g_array_index (priv->groups, CellGroup, group_allocs[i].group_idx); + + /* Exception for single cell groups */ + if (group->n_cells == 1) + { + CellInfo *info = group->cells->data; + AllocatedCell *cell; + int cell_position, cell_size; + + if (!gtk_cell_renderer_get_visible (info->renderer)) + continue; + + /* If were not aligned, place the cell after the last cell */ + if (info->align) + position = cell_position = group_allocs[i].position; + else + cell_position = position; + + /* If not a fixed size, use only the requested size for this row */ + if (info->fixed) + cell_size = group_allocs[i].size; + else + { + int dummy; + gtk_cell_area_request_renderer (area, info->renderer, + priv->orientation, + widget, for_size, + &dummy, + &cell_size); + cell_size = MIN (cell_size, group_allocs[i].size); + } + + if (rtl) + cell = allocated_cell_new (info->renderer, + full_size - (cell_position + cell_size), cell_size); + else + cell = allocated_cell_new (info->renderer, cell_position, cell_size); + + position += cell_size; + position += priv->spacing; + + allocated_cells = g_slist_prepend (allocated_cells, cell); + } + else + { + GtkRequestedSize *sizes; + int avail_size, cell_position; + int visible_cells, expand_cells; + int extra_size, extra_extra; + + visible_cells = count_visible_cells (group, &expand_cells); + + /* If this row has no visible cells in this group, just + * skip the allocation + */ + if (visible_cells == 0) + continue; + + /* If were not aligned, place the cell after the last cell + * and eat up the extra space + */ + if (group->align) + { + avail_size = group_allocs[i].size; + position = cell_position = group_allocs[i].position; + } + else + { + avail_size = group_allocs[i].size + (group_allocs[i].position - position); + cell_position = position; + } + + sizes = g_new (GtkRequestedSize, visible_cells); + + for (j = 0, cell_list = group->cells; cell_list; cell_list = cell_list->next) + { + CellInfo *info = cell_list->data; + + if (!gtk_cell_renderer_get_visible (info->renderer)) + continue; + + gtk_cell_area_request_renderer (area, info->renderer, + priv->orientation, + widget, for_size, + &sizes[j].minimum_size, + &sizes[j].natural_size); + + sizes[j].data = info; + avail_size -= sizes[j].minimum_size; + + j++; + } + + /* Distribute cells naturally within the group */ + avail_size -= (visible_cells - 1) * priv->spacing; + if (avail_size > 0) + avail_size = gtk_distribute_natural_allocation (avail_size, visible_cells, sizes); + else + avail_size = 0; + + /* Calculate/distribute expand for cells */ + if (expand_cells > 0) + { + extra_size = avail_size / expand_cells; + extra_extra = avail_size % expand_cells; + } + else + extra_size = extra_extra = 0; + + /* Create the allocated cells (loop only over visible cells here) */ + for (j = 0; j < visible_cells; j++) + { + CellInfo *info = sizes[j].data; + AllocatedCell *cell; + + if (info->expand) + { + sizes[j].minimum_size += extra_size; + if (extra_extra) + { + sizes[j].minimum_size++; + extra_extra--; + } + } + + if (rtl) + cell = allocated_cell_new (info->renderer, + full_size - (cell_position + sizes[j].minimum_size), + sizes[j].minimum_size); + else + cell = allocated_cell_new (info->renderer, cell_position, sizes[j].minimum_size); + + allocated_cells = g_slist_prepend (allocated_cells, cell); + + cell_position += sizes[j].minimum_size; + cell_position += priv->spacing; + } + + g_free (sizes); + + position = cell_position; + } + } + + g_free (group_allocs); + + /* Note it might not be important to reverse the list here at all, + * we have the correct positions, no need to allocate from left to right + */ + return g_slist_reverse (allocated_cells); +} + + +static void +gtk_cell_area_box_focus_changed (GtkCellArea *area, + GParamSpec *pspec, + GtkCellAreaBox *box) +{ + GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); + + if (gtk_cell_area_get_focus_cell (area)) + priv->last_focus_cell = gtk_cell_area_get_focus_cell (area); +} + +/************************************************************* + * GObjectClass * + *************************************************************/ +static void +gtk_cell_area_box_finalize (GObject *object) +{ + GtkCellAreaBox *box = GTK_CELL_AREA_BOX (object); + GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); + GSList *l; + + /* Unref/free the context list */ + for (l = priv->contexts; l; l = l->next) + g_object_weak_unref (G_OBJECT (l->data), (GWeakNotify)context_weak_notify, box); + + g_slist_free (priv->contexts); + priv->contexts = NULL; + + /* Free the cell grouping info */ + cell_groups_clear (box); + g_array_free (priv->groups, TRUE); + + G_OBJECT_CLASS (gtk_cell_area_box_parent_class)->finalize (object); +} + +static void +gtk_cell_area_box_dispose (GObject *object) +{ + G_OBJECT_CLASS (gtk_cell_area_box_parent_class)->dispose (object); +} + +static void +gtk_cell_area_box_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkCellAreaBox *box = GTK_CELL_AREA_BOX (object); + GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); + + switch (prop_id) + { + case PROP_ORIENTATION: + if (priv->orientation != g_value_get_enum (value)) + { + priv->orientation = g_value_get_enum (value); + /* Notify that size needs to be requested again */ + reset_contexts (box); + g_object_notify_by_pspec (object, pspec); + } + break; + case PROP_SPACING: + gtk_cell_area_box_set_spacing (box, g_value_get_int (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_cell_area_box_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkCellAreaBox *box = GTK_CELL_AREA_BOX (object); + GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); + + switch (prop_id) + { + case PROP_ORIENTATION: + g_value_set_enum (value, priv->orientation); + break; + case PROP_SPACING: + g_value_set_int (value, gtk_cell_area_box_get_spacing (box)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +/************************************************************* + * GtkCellAreaClass * + *************************************************************/ +static void +gtk_cell_area_box_add (GtkCellArea *area, + GtkCellRenderer *renderer) +{ + gtk_cell_area_box_pack_start (GTK_CELL_AREA_BOX (area), + renderer, FALSE, FALSE, TRUE); +} + +static void +gtk_cell_area_box_remove (GtkCellArea *area, + GtkCellRenderer *renderer) +{ + GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area); + GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); + GList *node; + + if (priv->last_focus_cell == renderer) + priv->last_focus_cell = NULL; + + node = g_list_find_custom (priv->cells, renderer, + (GCompareFunc)cell_info_find); + + if (node) + { + CellInfo *info = node->data; + + cell_info_free (info); + + priv->cells = g_list_delete_link (priv->cells, node); + + /* Reconstruct cell groups */ + cell_groups_rebuild (box); + } + else + g_warning ("Trying to remove a cell renderer that is not present GtkCellAreaBox"); +} + +static void +gtk_cell_area_box_foreach (GtkCellArea *area, + GtkCellCallback callback, + gpointer callback_data) +{ + GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area); + GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); + GList *list; + + for (list = priv->cells; list; list = list->next) + { + CellInfo *info = list->data; + + if (callback (info->renderer, callback_data)) + break; + } +} + +static void +gtk_cell_area_box_foreach_alloc (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + const GdkRectangle *cell_area, + const GdkRectangle *background_area, + GtkCellAllocCallback callback, + gpointer callback_data) +{ + GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area); + GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); + GtkCellAreaBoxContext *box_context = GTK_CELL_AREA_BOX_CONTEXT (context); + GSList *allocated_cells, *l; + GdkRectangle cell_alloc, cell_background; + gboolean rtl; + + rtl = (priv->orientation == GTK_ORIENTATION_HORIZONTAL && + gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL); + + cell_alloc = *cell_area; + + /* Get a list of cells with allocation sizes decided regardless + * of alignments and pack order etc. + */ + allocated_cells = get_allocated_cells (box, box_context, widget, + cell_area->width, cell_area->height); + + for (l = allocated_cells; l; l = l->next) + { + AllocatedCell *cell = l->data; + + if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) + { + cell_alloc.x = cell_area->x + cell->position; + cell_alloc.width = cell->size; + } + else + { + cell_alloc.y = cell_area->y + cell->position; + cell_alloc.height = cell->size; + } + + /* Stop iterating over cells if they flow out of the render + * area, this can happen because the render area can actually + * be smaller than the requested area (treeview columns can + * be user resizable and can be resized to be smaller than + * the actual requested area). + */ + if (cell_alloc.x > cell_area->x + cell_area->width || + cell_alloc.x + cell_alloc.width < cell_area->x || + cell_alloc.y > cell_area->y + cell_area->height) + break; + + /* Special case for the last cell (or first cell in rtl)... + * let the last cell consume the remaining space in the area + * (the last cell is allowed to consume the remaining space if + * the space given for rendering is actually larger than allocation, + * this can happen in the expander GtkTreeViewColumn where only the + * deepest depth column receives the allocation... shallow columns + * receive more width). */ + if (!l->next) + { + if (rtl) + { + /* Fill the leading space for the first cell in the area + * (still last in the list) + */ + cell_alloc.width = (cell_alloc.x - cell_area->x) + cell_alloc.width; + cell_alloc.x = cell_area->x; + } + else + { + cell_alloc.width = cell_area->x + cell_area->width - cell_alloc.x; + cell_alloc.height = cell_area->y + cell_area->height - cell_alloc.y; + } + } + else + { + /* If the cell we are rendering doesn't fit into the remaining space, + * clip it so that the underlying renderer has a chance to deal with + * it (for instance text renderers get a chance to ellipsize). + */ + if (cell_alloc.x + cell_alloc.width > cell_area->x + cell_area->width) + cell_alloc.width = cell_area->x + cell_area->width - cell_alloc.x; + + if (cell_alloc.y + cell_alloc.height > cell_area->y + cell_area->height) + cell_alloc.height = cell_area->y + cell_area->height - cell_alloc.y; + } + + /* Add portions of the background_area to the cell_alloc + * to create the cell_background + */ + cell_background = cell_alloc; + + if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) + { + if (l == allocated_cells) + { + /* Add the depth to the first cell */ + if (rtl) + { + cell_background.width += background_area->width - cell_area->width; + cell_background.x = background_area->x + background_area->width - cell_background.width; + } + else + { + cell_background.width += cell_area->x - background_area->x; + cell_background.x = background_area->x; + } + } + + if (l->next == NULL) + { + /* Grant this cell the remaining space */ + int remain = cell_background.x - background_area->x; + + if (rtl) + cell_background.x -= remain; + else + cell_background.width = background_area->width - remain; + } + + cell_background.y = background_area->y; + cell_background.height = background_area->height; + } + else + { + if (l == allocated_cells) + { + cell_background.height += cell_background.y - background_area->y; + cell_background.y = background_area->y; + } + + if (l->next == NULL) + cell_background.height = + background_area->height - (cell_background.y - background_area->y); + + cell_background.x = background_area->x; + cell_background.width = background_area->width; + } + + if (callback (cell->renderer, &cell_alloc, &cell_background, callback_data)) + break; + } + + g_slist_free_full (allocated_cells, (GDestroyNotify)allocated_cell_free); +} + +static void +gtk_cell_area_box_apply_attributes (GtkCellArea *area, + GtkTreeModel *tree_model, + GtkTreeIter *iter, + gboolean is_expander, + gboolean is_expanded) +{ + GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area); + GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); + int i; + + /* Call the parent class to apply the attributes */ + GTK_CELL_AREA_CLASS + (gtk_cell_area_box_parent_class)->apply_attributes (area, tree_model, iter, + is_expander, is_expanded); + + /* Update visible state for cell groups */ + for (i = 0; i < priv->groups->len; i++) + { + CellGroup *group = &g_array_index (priv->groups, CellGroup, i); + GList *list; + + group->visible = FALSE; + + for (list = group->cells; list && group->visible == FALSE; list = list->next) + { + CellInfo *info = list->data; + + if (gtk_cell_renderer_get_visible (info->renderer)) + group->visible = TRUE; + } + } +} + +static void +gtk_cell_area_box_set_cell_property (GtkCellArea *area, + GtkCellRenderer *renderer, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area); + GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); + GList *node; + CellInfo *info; + gboolean rebuild = FALSE; + gboolean val; + GtkPackType pack_type; + + node = g_list_find_custom (priv->cells, renderer, + (GCompareFunc)cell_info_find); + if (!node) + return; + + info = node->data; + + switch (prop_id) + { + case CELL_PROP_EXPAND: + val = g_value_get_boolean (value); + + if (info->expand != val) + { + info->expand = val; + rebuild = TRUE; + } + break; + + case CELL_PROP_ALIGN: + val = g_value_get_boolean (value); + + if (info->align != val) + { + info->align = val; + rebuild = TRUE; + } + break; + + case CELL_PROP_FIXED_SIZE: + val = g_value_get_boolean (value); + + if (info->fixed != val) + { + info->fixed = val; + rebuild = TRUE; + } + break; + + case CELL_PROP_PACK_TYPE: + pack_type = g_value_get_enum (value); + + if (info->pack != pack_type) + { + info->pack = pack_type; + rebuild = TRUE; + } + break; + default: + GTK_CELL_AREA_WARN_INVALID_CELL_PROPERTY_ID (area, prop_id, pspec); + break; + } + + /* Groups need to be rebuilt */ + if (rebuild) + cell_groups_rebuild (box); +} + +static void +gtk_cell_area_box_get_cell_property (GtkCellArea *area, + GtkCellRenderer *renderer, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area); + GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); + GList *node; + CellInfo *info; + + node = g_list_find_custom (priv->cells, renderer, + (GCompareFunc)cell_info_find); + if (!node) + return; + + info = node->data; + + switch (prop_id) + { + case CELL_PROP_EXPAND: + g_value_set_boolean (value, info->expand); + break; + + case CELL_PROP_ALIGN: + g_value_set_boolean (value, info->align); + break; + + case CELL_PROP_FIXED_SIZE: + g_value_set_boolean (value, info->fixed); + break; + + case CELL_PROP_PACK_TYPE: + g_value_set_enum (value, info->pack); + break; + default: + GTK_CELL_AREA_WARN_INVALID_CELL_PROPERTY_ID (area, prop_id, pspec); + break; + } +} + + +static GtkCellAreaContext * +gtk_cell_area_box_create_context (GtkCellArea *area) +{ + GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area); + GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); + GtkCellAreaContext *context = + (GtkCellAreaContext *)g_object_new (GTK_TYPE_CELL_AREA_BOX_CONTEXT, + "area", area, NULL); + + priv->contexts = g_slist_prepend (priv->contexts, context); + + g_object_weak_ref (G_OBJECT (context), (GWeakNotify)context_weak_notify, box); + + /* Tell the new group about our cell layout */ + init_context_group (box, GTK_CELL_AREA_BOX_CONTEXT (context)); + + return context; +} + +static GtkCellAreaContext * +gtk_cell_area_box_copy_context (GtkCellArea *area, + GtkCellAreaContext *context) +{ + GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area); + GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); + GtkCellAreaContext *copy = + (GtkCellAreaContext *)_gtk_cell_area_box_context_copy (GTK_CELL_AREA_BOX (area), + GTK_CELL_AREA_BOX_CONTEXT (context)); + + priv->contexts = g_slist_prepend (priv->contexts, copy); + + g_object_weak_ref (G_OBJECT (copy), (GWeakNotify)context_weak_notify, box); + + return copy; +} + +static GtkSizeRequestMode +gtk_cell_area_box_get_request_mode (GtkCellArea *area) +{ + GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area); + GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); + + return (priv->orientation) == GTK_ORIENTATION_HORIZONTAL ? + GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH : + GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT; +} + +static void +compute_size (GtkCellAreaBox *box, + GtkOrientation orientation, + GtkCellAreaBoxContext *context, + GtkWidget *widget, + int for_size, + int *minimum_size, + int *natural_size) +{ + GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); + GtkCellArea *area = GTK_CELL_AREA (box); + GList *list; + int i; + int min_size = 0; + int nat_size = 0; + + for (i = 0; i < priv->groups->len; i++) + { + CellGroup *group = &g_array_index (priv->groups, CellGroup, i); + int group_min_size = 0; + int group_nat_size = 0; + + for (list = group->cells; list; list = list->next) + { + CellInfo *info = list->data; + int renderer_min_size, renderer_nat_size; + + if (!gtk_cell_renderer_get_visible (info->renderer)) + continue; + + gtk_cell_area_request_renderer (area, info->renderer, orientation, widget, for_size, + &renderer_min_size, &renderer_nat_size); + + if (orientation == priv->orientation) + { + if (min_size > 0) + { + min_size += priv->spacing; + nat_size += priv->spacing; + } + + if (group_min_size > 0) + { + group_min_size += priv->spacing; + group_nat_size += priv->spacing; + } + + min_size += renderer_min_size; + nat_size += renderer_nat_size; + group_min_size += renderer_min_size; + group_nat_size += renderer_nat_size; + } + else + { + min_size = MAX (min_size, renderer_min_size); + nat_size = MAX (nat_size, renderer_nat_size); + group_min_size = MAX (group_min_size, renderer_min_size); + group_nat_size = MAX (group_nat_size, renderer_nat_size); + } + } + + if (orientation == GTK_ORIENTATION_HORIZONTAL) + { + if (for_size < 0) + _gtk_cell_area_box_context_push_group_width (context, group->id, group_min_size, group_nat_size); + else + _gtk_cell_area_box_context_push_group_width_for_height (context, group->id, for_size, + group_min_size, group_nat_size); + } + else + { + if (for_size < 0) + _gtk_cell_area_box_context_push_group_height (context, group->id, group_min_size, group_nat_size); + else + _gtk_cell_area_box_context_push_group_height_for_width (context, group->id, for_size, + group_min_size, group_nat_size); + } + } + + *minimum_size = min_size; + *natural_size = nat_size; + + /* Update rtl state for focus navigation to work */ + priv->rtl = (priv->orientation == GTK_ORIENTATION_HORIZONTAL && + gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL); +} + +static GtkRequestedSize * +get_group_sizes (GtkCellArea *area, + CellGroup *group, + GtkOrientation orientation, + GtkWidget *widget, + int *n_sizes) +{ + GtkRequestedSize *sizes; + GList *l; + int i; + + *n_sizes = count_visible_cells (group, NULL); + sizes = g_new (GtkRequestedSize, *n_sizes); + + for (l = group->cells, i = 0; l; l = l->next) + { + CellInfo *info = l->data; + + if (!gtk_cell_renderer_get_visible (info->renderer)) + continue; + + sizes[i].data = info; + + gtk_cell_area_request_renderer (area, info->renderer, + orientation, widget, -1, + &sizes[i].minimum_size, + &sizes[i].natural_size); + + i++; + } + + return sizes; +} + +static void +compute_group_size_for_opposing_orientation (GtkCellAreaBox *box, + CellGroup *group, + GtkWidget *widget, + int for_size, + int *minimum_size, + int *natural_size) +{ + GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); + GtkCellArea *area = GTK_CELL_AREA (box); + + /* Exception for single cell groups */ + if (group->n_cells == 1) + { + CellInfo *info = group->cells->data; + + gtk_cell_area_request_renderer (area, info->renderer, + OPPOSITE_ORIENTATION (priv->orientation), + widget, for_size, minimum_size, natural_size); + } + else + { + GtkRequestedSize *orientation_sizes; + CellInfo *info; + int n_sizes, i; + int avail_size = for_size; + int extra_size, extra_extra; + int min_size = 0, nat_size = 0; + + orientation_sizes = get_group_sizes (area, group, priv->orientation, widget, &n_sizes); + + /* First naturally allocate the cells in the group into the for_size */ + avail_size -= (n_sizes - 1) * priv->spacing; + for (i = 0; i < n_sizes; i++) + avail_size -= orientation_sizes[i].minimum_size; + + if (avail_size > 0) + avail_size = gtk_distribute_natural_allocation (avail_size, n_sizes, orientation_sizes); + else + avail_size = 0; + + /* Calculate/distribute expand for cells */ + if (group->expand_cells > 0) + { + extra_size = avail_size / group->expand_cells; + extra_extra = avail_size % group->expand_cells; + } + else + extra_size = extra_extra = 0; + + for (i = 0; i < n_sizes; i++) + { + int cell_min, cell_nat; + + info = orientation_sizes[i].data; + + if (info->expand) + { + orientation_sizes[i].minimum_size += extra_size; + if (extra_extra) + { + orientation_sizes[i].minimum_size++; + extra_extra--; + } + } + + gtk_cell_area_request_renderer (area, info->renderer, + OPPOSITE_ORIENTATION (priv->orientation), + widget, + orientation_sizes[i].minimum_size, + &cell_min, &cell_nat); + + min_size = MAX (min_size, cell_min); + nat_size = MAX (nat_size, cell_nat); + } + + *minimum_size = min_size; + *natural_size = nat_size; + + g_free (orientation_sizes); + } +} + +static void +compute_size_for_opposing_orientation (GtkCellAreaBox *box, + GtkCellAreaBoxContext *context, + GtkWidget *widget, + int for_size, + int *minimum_size, + int *natural_size) +{ + GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); + CellGroup *group; + GtkRequestedSize *orientation_sizes; + int n_groups, n_expand_groups, i; + int avail_size = for_size; + int extra_size, extra_extra; + int min_size = 0, nat_size = 0; + + n_expand_groups = count_expand_groups (box); + + if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) + orientation_sizes = _gtk_cell_area_box_context_get_widths (context, &n_groups); + else + orientation_sizes = _gtk_cell_area_box_context_get_heights (context, &n_groups); + + /* First start by naturally allocating space among groups of cells */ + avail_size -= (n_groups - 1) * priv->spacing; + for (i = 0; i < n_groups; i++) + avail_size -= orientation_sizes[i].minimum_size; + + if (avail_size > 0) + avail_size = gtk_distribute_natural_allocation (avail_size, n_groups, orientation_sizes); + else + avail_size = 0; + + /* Calculate/distribute expand for groups */ + if (n_expand_groups > 0) + { + extra_size = avail_size / n_expand_groups; + extra_extra = avail_size % n_expand_groups; + } + else + extra_size = extra_extra = 0; + + /* Now we need to naturally allocate sizes for cells in each group + * and push the height-for-width for each group accordingly while + * accumulating the overall height-for-width for this row. + */ + for (i = 0; i < n_groups; i++) + { + int group_min, group_nat; + int group_idx = GPOINTER_TO_INT (orientation_sizes[i].data); + + group = &g_array_index (priv->groups, CellGroup, group_idx); + + if (group->expand_cells > 0) + { + orientation_sizes[i].minimum_size += extra_size; + if (extra_extra) + { + orientation_sizes[i].minimum_size++; + extra_extra--; + } + } + + /* Now we have the allocation for the group, + * request its height-for-width + */ + compute_group_size_for_opposing_orientation (box, group, widget, + orientation_sizes[i].minimum_size, + &group_min, &group_nat); + + min_size = MAX (min_size, group_min); + nat_size = MAX (nat_size, group_nat); + + if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) + { + _gtk_cell_area_box_context_push_group_height_for_width (context, group_idx, for_size, + group_min, group_nat); + } + else + { + _gtk_cell_area_box_context_push_group_width_for_height (context, group_idx, for_size, + group_min, group_nat); + } + } + + *minimum_size = min_size; + *natural_size = nat_size; + + g_free (orientation_sizes); + + /* Update rtl state for focus navigation to work */ + priv->rtl = (priv->orientation == GTK_ORIENTATION_HORIZONTAL && + gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL); +} + + + +static void +gtk_cell_area_box_get_preferred_width (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + int *minimum_width, + int *natural_width) +{ + GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area); + GtkCellAreaBoxContext *box_context; + int min_width, nat_width; + + g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (context)); + + box_context = GTK_CELL_AREA_BOX_CONTEXT (context); + + /* Compute the size of all renderers for current row data, + * bumping cell alignments in the context along the way + */ + compute_size (box, GTK_ORIENTATION_HORIZONTAL, + box_context, widget, -1, &min_width, &nat_width); + + if (minimum_width) + *minimum_width = min_width; + + if (natural_width) + *natural_width = nat_width; +} + +static void +gtk_cell_area_box_get_preferred_height (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + int *minimum_height, + int *natural_height) +{ + GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area); + GtkCellAreaBoxContext *box_context; + int min_height, nat_height; + + g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (context)); + + box_context = GTK_CELL_AREA_BOX_CONTEXT (context); + + /* Compute the size of all renderers for current row data, + * bumping cell alignments in the context along the way + */ + compute_size (box, GTK_ORIENTATION_VERTICAL, + box_context, widget, -1, &min_height, &nat_height); + + if (minimum_height) + *minimum_height = min_height; + + if (natural_height) + *natural_height = nat_height; +} + +static void +gtk_cell_area_box_get_preferred_height_for_width (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + int width, + int *minimum_height, + int *natural_height) +{ + GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area); + GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); + GtkCellAreaBoxContext *box_context = GTK_CELL_AREA_BOX_CONTEXT (context); + int min_height, nat_height; + + g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (context)); + + if (priv->orientation == GTK_ORIENTATION_VERTICAL) + { + /* Add up vertical requests of height for width and push + * the overall cached sizes for alignments + */ + compute_size (box, priv->orientation, box_context, widget, width, &min_height, &nat_height); + } + else + { + /* Juice: virtually allocate cells into the for_width using the + * alignments and then return the overall height for that width, + * and cache it + */ + compute_size_for_opposing_orientation (box, box_context, widget, width, &min_height, &nat_height); + } + + if (minimum_height) + *minimum_height = min_height; + + if (natural_height) + *natural_height = nat_height; +} + +static void +gtk_cell_area_box_get_preferred_width_for_height (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + int height, + int *minimum_width, + int *natural_width) +{ + GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area); + GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); + GtkCellAreaBoxContext *box_context = GTK_CELL_AREA_BOX_CONTEXT (context); + int min_width, nat_width; + + g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (context)); + + if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) + { + /* Add up horizontal requests of width for height and push + * the overall cached sizes for alignments + */ + compute_size (box, priv->orientation, box_context, widget, height, &min_width, &nat_width); + } + else + { + /* Juice: horizontally allocate cells into the for_height using the + * alignments and then return the overall width for that height, + * and cache it + */ + compute_size_for_opposing_orientation (box, box_context, widget, height, &min_width, &nat_width); + } + + if (minimum_width) + *minimum_width = min_width; + + if (natural_width) + *natural_width = nat_width; +} + +enum { + FOCUS_NONE, + FOCUS_PREV, + FOCUS_NEXT, + FOCUS_LAST_CELL +}; + +static gboolean +gtk_cell_area_box_focus (GtkCellArea *area, + GtkDirectionType direction) +{ + GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area); + GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); + int cycle = FOCUS_NONE; + gboolean cycled_focus = FALSE; + GtkCellRenderer *focus_cell; + + focus_cell = gtk_cell_area_get_focus_cell (area); + + /* Special case, when there is no activatable cell, focus + * is painted around the entire area... in this case we + * let focus leave the area directly. + */ + if (focus_cell && !gtk_cell_area_is_activatable (area)) + { + gtk_cell_area_set_focus_cell (area, NULL); + return FALSE; + } + + switch (direction) + { + case GTK_DIR_TAB_FORWARD: + cycle = priv->rtl ? FOCUS_PREV : FOCUS_NEXT; + break; + case GTK_DIR_TAB_BACKWARD: + cycle = priv->rtl ? FOCUS_NEXT : FOCUS_PREV; + break; + case GTK_DIR_UP: + if (priv->orientation == GTK_ORIENTATION_VERTICAL || !priv->last_focus_cell) + cycle = FOCUS_PREV; + else if (!focus_cell) + cycle = FOCUS_LAST_CELL; + break; + case GTK_DIR_DOWN: + if (priv->orientation == GTK_ORIENTATION_VERTICAL || !priv->last_focus_cell) + cycle = FOCUS_NEXT; + else if (!focus_cell) + cycle = FOCUS_LAST_CELL; + break; + case GTK_DIR_LEFT: + if (priv->orientation == GTK_ORIENTATION_HORIZONTAL || !priv->last_focus_cell) + cycle = priv->rtl ? FOCUS_NEXT : FOCUS_PREV; + else if (!focus_cell) + cycle = FOCUS_LAST_CELL; + break; + case GTK_DIR_RIGHT: + if (priv->orientation == GTK_ORIENTATION_HORIZONTAL || !priv->last_focus_cell) + cycle = priv->rtl ? FOCUS_PREV : FOCUS_NEXT; + else if (!focus_cell) + cycle = FOCUS_LAST_CELL; + break; + default: + break; + } + + if (cycle == FOCUS_LAST_CELL) + { + gtk_cell_area_set_focus_cell (area, priv->last_focus_cell); + cycled_focus = TRUE; + } + else if (cycle != FOCUS_NONE) + { + gboolean found_cell = FALSE; + GList *list; + int i; + + /* If there is no focused cell, focus on the first (or last) one */ + if (!focus_cell) + found_cell = TRUE; + + for (i = (cycle == FOCUS_NEXT) ? 0 : priv->groups->len -1; + cycled_focus == FALSE && i >= 0 && i < priv->groups->len; + i = (cycle == FOCUS_NEXT) ? i + 1 : i - 1) + { + CellGroup *group = &g_array_index (priv->groups, CellGroup, i); + + for (list = (cycle == FOCUS_NEXT) ? g_list_first (group->cells) : g_list_last (group->cells); + cycled_focus == FALSE && list; list = (cycle == FOCUS_NEXT) ? list->next : list->prev) + { + CellInfo *info = list->data; + + if (info->renderer == focus_cell) + found_cell = TRUE; + else if (found_cell && /* Dont give focus to cells that are siblings to a focus cell */ + gtk_cell_area_get_focus_from_sibling (area, info->renderer) == NULL) + { + gtk_cell_area_set_focus_cell (area, info->renderer); + cycled_focus = TRUE; + } + } + } + } + + if (!cycled_focus) + gtk_cell_area_set_focus_cell (area, NULL); + + return cycled_focus; +} + + +/************************************************************* + * GtkCellLayoutIface * + *************************************************************/ +static void +gtk_cell_area_box_cell_layout_init (GtkCellLayoutIface *iface) +{ + iface->pack_start = gtk_cell_area_box_layout_pack_start; + iface->pack_end = gtk_cell_area_box_layout_pack_end; + iface->reorder = gtk_cell_area_box_layout_reorder; +} + +static void +gtk_cell_area_box_layout_pack_start (GtkCellLayout *cell_layout, + GtkCellRenderer *renderer, + gboolean expand) +{ + gtk_cell_area_box_pack_start (GTK_CELL_AREA_BOX (cell_layout), renderer, expand, FALSE, TRUE); +} + +static void +gtk_cell_area_box_layout_pack_end (GtkCellLayout *cell_layout, + GtkCellRenderer *renderer, + gboolean expand) +{ + gtk_cell_area_box_pack_end (GTK_CELL_AREA_BOX (cell_layout), renderer, expand, FALSE, TRUE); +} + +static void +gtk_cell_area_box_layout_reorder (GtkCellLayout *cell_layout, + GtkCellRenderer *renderer, + int position) +{ + GtkCellAreaBox *box = GTK_CELL_AREA_BOX (cell_layout); + GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); + GList *node; + CellInfo *info; + + node = g_list_find_custom (priv->cells, renderer, + (GCompareFunc)cell_info_find); + + if (node) + { + info = node->data; + + priv->cells = g_list_delete_link (priv->cells, node); + priv->cells = g_list_insert (priv->cells, info, position); + + cell_groups_rebuild (box); + } +} + +/************************************************************* + * Private interaction with GtkCellAreaBoxContext * + *************************************************************/ +gboolean +_gtk_cell_area_box_group_visible (GtkCellAreaBox *box, + int group_idx) +{ + GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); + CellGroup *group; + + g_assert (group_idx >= 0 && group_idx < priv->groups->len); + + group = &g_array_index (priv->groups, CellGroup, group_idx); + + return group->visible; +} + + +/************************************************************* + * API * + *************************************************************/ +/** + * gtk_cell_area_box_new: + * + * Creates a new `GtkCellAreaBox`. + * + * Returns: a newly created `GtkCellAreaBox` + * + * Deprecated: 4.10 + */ +GtkCellArea * +gtk_cell_area_box_new (void) +{ + return (GtkCellArea *)g_object_new (GTK_TYPE_CELL_AREA_BOX, NULL); +} + +/** + * gtk_cell_area_box_pack_start: + * @box: a `GtkCellAreaBox` + * @renderer: the `GtkCellRenderer` to add + * @expand: whether @renderer should receive extra space when the area receives + * more than its natural size + * @align: whether @renderer should be aligned in adjacent rows + * @fixed: whether @renderer should have the same size in all rows + * + * Adds @renderer to @box, packed with reference to the start of @box. + * + * The @renderer is packed after any other `GtkCellRenderer` packed + * with reference to the start of @box. + * + * Deprecated: 4.10 + */ +void +gtk_cell_area_box_pack_start (GtkCellAreaBox *box, + GtkCellRenderer *renderer, + gboolean expand, + gboolean align, + gboolean fixed) +{ + GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); + CellInfo *info; + + g_return_if_fail (GTK_IS_CELL_AREA_BOX (box)); + g_return_if_fail (GTK_IS_CELL_RENDERER (renderer)); + + if (g_list_find_custom (priv->cells, renderer, + (GCompareFunc)cell_info_find)) + { + g_warning ("Refusing to add the same cell renderer to a GtkCellAreaBox twice"); + return; + } + + info = cell_info_new (renderer, GTK_PACK_START, expand, align, fixed); + + priv->cells = g_list_append (priv->cells, info); + + cell_groups_rebuild (box); +} + +/** + * gtk_cell_area_box_pack_end: + * @box: a `GtkCellAreaBox` + * @renderer: the `GtkCellRenderer` to add + * @expand: whether @renderer should receive extra space when the area receives + * more than its natural size + * @align: whether @renderer should be aligned in adjacent rows + * @fixed: whether @renderer should have the same size in all rows + * + * Adds @renderer to @box, packed with reference to the end of @box. + * + * The @renderer is packed after (away from end of) any other + * `GtkCellRenderer` packed with reference to the end of @box. + * + * Deprecated: 4.10 + */ +void +gtk_cell_area_box_pack_end (GtkCellAreaBox *box, + GtkCellRenderer *renderer, + gboolean expand, + gboolean align, + gboolean fixed) +{ + GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); + CellInfo *info; + + g_return_if_fail (GTK_IS_CELL_AREA_BOX (box)); + g_return_if_fail (GTK_IS_CELL_RENDERER (renderer)); + + if (g_list_find_custom (priv->cells, renderer, + (GCompareFunc)cell_info_find)) + { + g_warning ("Refusing to add the same cell renderer to a GtkCellArea twice"); + return; + } + + info = cell_info_new (renderer, GTK_PACK_END, expand, align, fixed); + + priv->cells = g_list_append (priv->cells, info); + + cell_groups_rebuild (box); +} + +/** + * gtk_cell_area_box_get_spacing: + * @box: a `GtkCellAreaBox` + * + * Gets the spacing added between cell renderers. + * + * Returns: the space added between cell renderers in @box. + * + * Deprecated: 4.10 + */ +int +gtk_cell_area_box_get_spacing (GtkCellAreaBox *box) +{ + GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); + + g_return_val_if_fail (GTK_IS_CELL_AREA_BOX (box), 0); + + return priv->spacing; +} + +/** + * gtk_cell_area_box_set_spacing: + * @box: a `GtkCellAreaBox` + * @spacing: the space to add between `GtkCellRenderer`s + * + * Sets the spacing to add between cell renderers in @box. + * + * Deprecated: 4.10 + */ +void +gtk_cell_area_box_set_spacing (GtkCellAreaBox *box, + int spacing) +{ + GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); + + g_return_if_fail (GTK_IS_CELL_AREA_BOX (box)); + + if (priv->spacing != spacing) + { + priv->spacing = spacing; + + g_object_notify (G_OBJECT (box), "spacing"); + + /* Notify that size needs to be requested again */ + reset_contexts (box); + } +} diff --git a/gtk/deprecated/gtkcellareabox.h b/gtk/deprecated/gtkcellareabox.h new file mode 100644 index 0000000000..ade84e0db4 --- /dev/null +++ b/gtk/deprecated/gtkcellareabox.h @@ -0,0 +1,70 @@ +/* gtkcellareabox.h + * + * Copyright (C) 2010 Openismus GmbH + * + * Authors: + * Tristan Van Berkom + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#ifndef __GTK_CELL_AREA_BOX_H__ +#define __GTK_CELL_AREA_BOX_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_CELL_AREA_BOX (gtk_cell_area_box_get_type ()) +#define GTK_CELL_AREA_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_AREA_BOX, GtkCellAreaBox)) +#define GTK_IS_CELL_AREA_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_AREA_BOX)) + +typedef struct _GtkCellAreaBox GtkCellAreaBox; + +GDK_AVAILABLE_IN_ALL +GType gtk_cell_area_box_get_type (void) G_GNUC_CONST; + +GDK_DEPRECATED_IN_4_10 +GtkCellArea *gtk_cell_area_box_new (void); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_area_box_pack_start (GtkCellAreaBox *box, + GtkCellRenderer *renderer, + gboolean expand, + gboolean align, + gboolean fixed); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_area_box_pack_end (GtkCellAreaBox *box, + GtkCellRenderer *renderer, + gboolean expand, + gboolean align, + gboolean fixed); +GDK_DEPRECATED_IN_4_10 +int gtk_cell_area_box_get_spacing (GtkCellAreaBox *box); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_area_box_set_spacing (GtkCellAreaBox *box, + int spacing); + +/* Private interaction with GtkCellAreaBoxContext */ +gboolean _gtk_cell_area_box_group_visible (GtkCellAreaBox *box, + int group_idx); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkCellAreaBox, g_object_unref) + +G_END_DECLS + +#endif /* __GTK_CELL_AREA_BOX_H__ */ diff --git a/gtk/deprecated/gtkcellareaboxcontext.c b/gtk/deprecated/gtkcellareaboxcontext.c new file mode 100644 index 0000000000..5bf0dd1238 --- /dev/null +++ b/gtk/deprecated/gtkcellareaboxcontext.c @@ -0,0 +1,860 @@ +/* gtkcellareaboxcontext.c + * + * Copyright (C) 2010 Openismus GmbH + * + * Authors: + * Tristan Van Berkom + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#include "config.h" +#include "gtkcellareabox.h" +#include "gtkcellareaboxcontextprivate.h" +#include "gtkorientable.h" + +#include "gtkprivate.h" + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + +/* GObjectClass */ +static void _gtk_cell_area_box_context_finalize (GObject *object); + +/* GtkCellAreaContextClass */ +static void _gtk_cell_area_box_context_reset (GtkCellAreaContext *context); +static void _gtk_cell_area_box_context_get_preferred_height_for_width (GtkCellAreaContext *context, + int width, + int *minimum_height, + int *natural_height); +static void _gtk_cell_area_box_context_get_preferred_width_for_height (GtkCellAreaContext *context, + int height, + int *minimum_width, + int *natural_width); + + + +/* Internal functions */ +static void _gtk_cell_area_box_context_sum (GtkCellAreaBoxContext *context, + GtkOrientation orientation, + int for_size, + int *minimum_size, + int *natural_size); +static void free_cache_array (GArray *array); +static GArray *group_array_new (GtkCellAreaBoxContext *context); +static GArray *get_array (GtkCellAreaBoxContext *context, + GtkOrientation orientation, + int for_size); +static gboolean group_expands (GtkCellAreaBoxContext *context, + int group_idx); +static int count_expand_groups (GtkCellAreaBoxContext *context); + + +/* CachedSize management */ +typedef struct { + int min_size; + int nat_size; +} CachedSize; + +struct _GtkCellAreaBoxContextPrivate +{ + /* Table of per renderer CachedSizes */ + GArray *base_widths; + GArray *base_heights; + + /* Table of per height/width hash tables of per renderer CachedSizes */ + GHashTable *widths; + GHashTable *heights; + + /* Whether each group expands */ + gboolean *expand; + + /* Whether each group is aligned */ + gboolean *align; +}; + +G_DEFINE_TYPE_WITH_PRIVATE (GtkCellAreaBoxContext, _gtk_cell_area_box_context, GTK_TYPE_CELL_AREA_CONTEXT) + +static void +free_cache_array (GArray *array) +{ + g_array_free (array, TRUE); +} + +static GArray * +group_array_new (GtkCellAreaBoxContext *context) +{ + GtkCellAreaBoxContextPrivate *priv = context->priv; + GArray *group_array; + + group_array = g_array_new (FALSE, TRUE, sizeof (CachedSize)); + g_array_set_size (group_array, priv->base_widths->len); + + return group_array; +} + +static GArray * +get_array (GtkCellAreaBoxContext *context, + GtkOrientation orientation, + int for_size) +{ + GtkCellAreaBoxContextPrivate *priv = context->priv; + GArray *array; + + if (for_size < 0) + { + if (orientation == GTK_ORIENTATION_HORIZONTAL) + array = priv->base_widths; + else + array = priv->base_heights; + } + else + { + if (orientation == GTK_ORIENTATION_HORIZONTAL) + { + array = g_hash_table_lookup (priv->widths, GINT_TO_POINTER (for_size)); + + if (!array) + array = priv->base_widths; + } + else + { + array = g_hash_table_lookup (priv->heights, GINT_TO_POINTER (for_size)); + + if (!array) + array = priv->base_heights; + } + } + + return array; +} + +static gboolean +group_expands (GtkCellAreaBoxContext *context, + int group_idx) +{ + GtkCellAreaBoxContextPrivate *priv = context->priv; + + g_assert (group_idx >= 0 && group_idx < priv->base_widths->len); + + return priv->expand[group_idx]; +} + +static int +count_expand_groups (GtkCellAreaBoxContext *context) +{ + GtkCellAreaBoxContextPrivate *priv = context->priv; + int i, expand = 0; + + for (i = 0; i < priv->base_widths->len; i++) + { + if (priv->expand[i]) + expand++; + } + + return expand; +} + +static void +_gtk_cell_area_box_context_init (GtkCellAreaBoxContext *box_context) +{ + GtkCellAreaBoxContextPrivate *priv; + + box_context->priv = _gtk_cell_area_box_context_get_instance_private (box_context); + priv = box_context->priv; + + priv->base_widths = g_array_new (FALSE, TRUE, sizeof (CachedSize)); + priv->base_heights = g_array_new (FALSE, TRUE, sizeof (CachedSize)); + + priv->widths = g_hash_table_new_full (g_direct_hash, g_direct_equal, + NULL, (GDestroyNotify)free_cache_array); + priv->heights = g_hash_table_new_full (g_direct_hash, g_direct_equal, + NULL, (GDestroyNotify)free_cache_array); +} + +static void +_gtk_cell_area_box_context_class_init (GtkCellAreaBoxContextClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + GtkCellAreaContextClass *context_class = GTK_CELL_AREA_CONTEXT_CLASS (class); + + /* GObjectClass */ + object_class->finalize = _gtk_cell_area_box_context_finalize; + + context_class->reset = _gtk_cell_area_box_context_reset; + context_class->get_preferred_height_for_width = _gtk_cell_area_box_context_get_preferred_height_for_width; + context_class->get_preferred_width_for_height = _gtk_cell_area_box_context_get_preferred_width_for_height; +} + +/************************************************************* + * GObjectClass * + *************************************************************/ +static void +_gtk_cell_area_box_context_finalize (GObject *object) +{ + GtkCellAreaBoxContext *box_context = GTK_CELL_AREA_BOX_CONTEXT (object); + GtkCellAreaBoxContextPrivate *priv = box_context->priv; + + g_array_free (priv->base_widths, TRUE); + g_array_free (priv->base_heights, TRUE); + g_hash_table_destroy (priv->widths); + g_hash_table_destroy (priv->heights); + + g_free (priv->expand); + g_free (priv->align); + + G_OBJECT_CLASS (_gtk_cell_area_box_context_parent_class)->finalize (object); +} + +/************************************************************* + * GtkCellAreaContextClass * + *************************************************************/ +static void +_gtk_cell_area_box_context_reset (GtkCellAreaContext *context) +{ + GtkCellAreaBoxContext *box_context = GTK_CELL_AREA_BOX_CONTEXT (context); + GtkCellAreaBoxContextPrivate *priv = box_context->priv; + CachedSize *size; + int i; + + for (i = 0; i < priv->base_widths->len; i++) + { + size = &g_array_index (priv->base_widths, CachedSize, i); + + size->min_size = 0; + size->nat_size = 0; + + size = &g_array_index (priv->base_heights, CachedSize, i); + + size->min_size = 0; + size->nat_size = 0; + } + + /* Reset context sizes as well */ + g_hash_table_remove_all (priv->widths); + g_hash_table_remove_all (priv->heights); + + GTK_CELL_AREA_CONTEXT_CLASS + (_gtk_cell_area_box_context_parent_class)->reset (context); +} + +static void +_gtk_cell_area_box_context_sum (GtkCellAreaBoxContext *context, + GtkOrientation orientation, + int for_size, + int *minimum_size, + int *natural_size) +{ + GtkCellAreaBoxContextPrivate *priv = context->priv; + GtkCellAreaBox *area; + GtkOrientation box_orientation; + GArray *array; + int spacing, i, last_aligned_group_idx; + int min_size = 0, nat_size = 0; + + area = (GtkCellAreaBox *)gtk_cell_area_context_get_area (GTK_CELL_AREA_CONTEXT (context)); + spacing = gtk_cell_area_box_get_spacing (area); + box_orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (area)); + array = get_array (context, orientation, for_size); + + /* Get the last visible aligned group + * (we need to get space at least up till this group) */ + for (i = array->len - 1; i >= 0; i--) + { + if (priv->align[i] && + _gtk_cell_area_box_group_visible (area, i)) + break; + } + last_aligned_group_idx = i >= 0 ? i : 0; + + for (i = 0; i < array->len; i++) + { + CachedSize *size = &g_array_index (array, CachedSize, i); + + if (box_orientation == orientation) + { + if (i > last_aligned_group_idx && + !_gtk_cell_area_box_group_visible (area, i)) + continue; + + /* Dont add spacing for 0 size groups, they can be 0 size because + * they contain only invisible cells for this round of requests + */ + if (min_size > 0 && size->nat_size > 0) + { + min_size += spacing; + nat_size += spacing; + } + + min_size += size->min_size; + nat_size += size->nat_size; + } + else + { + min_size = MAX (min_size, size->min_size); + nat_size = MAX (nat_size, size->nat_size); + } + } + + if (for_size < 0) + { + if (orientation == GTK_ORIENTATION_HORIZONTAL) + gtk_cell_area_context_push_preferred_width (GTK_CELL_AREA_CONTEXT (context), min_size, nat_size); + else + gtk_cell_area_context_push_preferred_height (GTK_CELL_AREA_CONTEXT (context), min_size, nat_size); + } + + if (minimum_size) + *minimum_size = min_size; + if (natural_size) + *natural_size = nat_size; +} + +static void +_gtk_cell_area_box_context_get_preferred_height_for_width (GtkCellAreaContext *context, + int width, + int *minimum_height, + int *natural_height) +{ + _gtk_cell_area_box_context_sum (GTK_CELL_AREA_BOX_CONTEXT (context), GTK_ORIENTATION_VERTICAL, + width, minimum_height, natural_height); +} + +static void +_gtk_cell_area_box_context_get_preferred_width_for_height (GtkCellAreaContext *context, + int height, + int *minimum_width, + int *natural_width) +{ + _gtk_cell_area_box_context_sum (GTK_CELL_AREA_BOX_CONTEXT (context), GTK_ORIENTATION_HORIZONTAL, + height, minimum_width, natural_width); +} + +/************************************************************* + * API * + *************************************************************/ +static void +copy_size_array (GArray *src_array, + GArray *dest_array) +{ + int i; + + for (i = 0; i < src_array->len; i++) + { + CachedSize *src = &g_array_index (src_array, CachedSize, i); + CachedSize *dest = &g_array_index (dest_array, CachedSize, i); + + memcpy (dest, src, sizeof (CachedSize)); + } +} + +static void +for_size_copy (gpointer key, + GArray *size_array, + GHashTable *dest_hash) +{ + GArray *new_array; + + new_array = g_array_new (FALSE, TRUE, sizeof (CachedSize)); + g_array_set_size (new_array, size_array->len); + + copy_size_array (size_array, new_array); + + g_hash_table_insert (dest_hash, key, new_array); +} + +GtkCellAreaBoxContext * +_gtk_cell_area_box_context_copy (GtkCellAreaBox *box, + GtkCellAreaBoxContext *context) +{ + GtkCellAreaBoxContext *copy; + + copy = g_object_new (GTK_TYPE_CELL_AREA_BOX_CONTEXT, + "area", box, NULL); + + _gtk_cell_area_box_init_groups (copy, + context->priv->base_widths->len, + context->priv->expand, + context->priv->align); + + /* Copy the base arrays */ + copy_size_array (context->priv->base_widths, + copy->priv->base_widths); + copy_size_array (context->priv->base_heights, + copy->priv->base_heights); + + /* Copy each for size */ + g_hash_table_foreach (context->priv->heights, + (GHFunc)for_size_copy, copy->priv->heights); + g_hash_table_foreach (context->priv->widths, + (GHFunc)for_size_copy, copy->priv->widths); + + + return copy; +} + +void +_gtk_cell_area_box_init_groups (GtkCellAreaBoxContext *box_context, + guint n_groups, + gboolean *expand_groups, + gboolean *align_groups) +{ + GtkCellAreaBoxContextPrivate *priv; + gsize groups_size; + + g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context)); + g_return_if_fail (n_groups == 0 || expand_groups != NULL); + + /* When the group dimensions change, all info must be reset + * Note this already clears the min/nat values on the CachedSizes + */ + gtk_cell_area_context_reset (GTK_CELL_AREA_CONTEXT (box_context)); + + priv = box_context->priv; + g_array_set_size (priv->base_widths, n_groups); + g_array_set_size (priv->base_heights, n_groups); + + groups_size = n_groups * sizeof (gboolean); + + g_free (priv->expand); + priv->expand = g_memdup2 (expand_groups, groups_size); + + g_free (priv->align); + priv->align = g_memdup2 (align_groups, groups_size); +} + +void +_gtk_cell_area_box_context_push_group_width (GtkCellAreaBoxContext *box_context, + int group_idx, + int minimum_width, + int natural_width) +{ + GtkCellAreaBoxContextPrivate *priv; + CachedSize *size; + gboolean grew = FALSE; + + g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context)); + + priv = box_context->priv; + g_return_if_fail (group_idx < priv->base_widths->len); + + size = &g_array_index (priv->base_widths, CachedSize, group_idx); + if (minimum_width > size->min_size) + { + size->min_size = minimum_width; + grew = TRUE; + } + if (natural_width > size->nat_size) + { + size->nat_size = natural_width; + grew = TRUE; + } + + if (grew) + _gtk_cell_area_box_context_sum (box_context, GTK_ORIENTATION_HORIZONTAL, -1, NULL, NULL); +} + +void +_gtk_cell_area_box_context_push_group_height_for_width (GtkCellAreaBoxContext *box_context, + int group_idx, + int for_width, + int minimum_height, + int natural_height) +{ + GtkCellAreaBoxContextPrivate *priv; + GArray *group_array; + CachedSize *size; + + g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context)); + + priv = box_context->priv; + g_return_if_fail (group_idx < priv->base_widths->len); + + group_array = g_hash_table_lookup (priv->heights, GINT_TO_POINTER (for_width)); + if (!group_array) + { + group_array = group_array_new (box_context); + g_hash_table_insert (priv->heights, GINT_TO_POINTER (for_width), group_array); + } + + size = &g_array_index (group_array, CachedSize, group_idx); + size->min_size = MAX (size->min_size, minimum_height); + size->nat_size = MAX (size->nat_size, natural_height); +} + +void +_gtk_cell_area_box_context_push_group_height (GtkCellAreaBoxContext *box_context, + int group_idx, + int minimum_height, + int natural_height) +{ + GtkCellAreaBoxContextPrivate *priv; + CachedSize *size; + gboolean grew = FALSE; + + g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context)); + + priv = box_context->priv; + g_return_if_fail (group_idx < priv->base_heights->len); + + size = &g_array_index (priv->base_heights, CachedSize, group_idx); + if (minimum_height > size->min_size) + { + size->min_size = minimum_height; + grew = TRUE; + } + if (natural_height > size->nat_size) + { + size->nat_size = natural_height; + grew = TRUE; + } + + if (grew) + _gtk_cell_area_box_context_sum (box_context, GTK_ORIENTATION_VERTICAL, -1, NULL, NULL); +} + +void +_gtk_cell_area_box_context_push_group_width_for_height (GtkCellAreaBoxContext *box_context, + int group_idx, + int for_height, + int minimum_width, + int natural_width) +{ + GtkCellAreaBoxContextPrivate *priv; + GArray *group_array; + CachedSize *size; + + g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context)); + + priv = box_context->priv; + g_return_if_fail (group_idx < priv->base_widths->len); + + group_array = g_hash_table_lookup (priv->widths, GINT_TO_POINTER (for_height)); + if (!group_array) + { + group_array = group_array_new (box_context); + g_hash_table_insert (priv->widths, GINT_TO_POINTER (for_height), group_array); + } + + size = &g_array_index (group_array, CachedSize, group_idx); + size->min_size = MAX (size->min_size, minimum_width); + size->nat_size = MAX (size->nat_size, natural_width); +} + +void +_gtk_cell_area_box_context_get_group_width (GtkCellAreaBoxContext *box_context, + int group_idx, + int *minimum_width, + int *natural_width) +{ + GtkCellAreaBoxContextPrivate *priv; + CachedSize *size; + + g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context)); + + priv = box_context->priv; + g_return_if_fail (group_idx < priv->base_widths->len); + + size = &g_array_index (priv->base_widths, CachedSize, group_idx); + + if (minimum_width) + *minimum_width = size->min_size; + + if (natural_width) + *natural_width = size->nat_size; +} + +void +_gtk_cell_area_box_context_get_group_height_for_width (GtkCellAreaBoxContext *box_context, + int group_idx, + int for_width, + int *minimum_height, + int *natural_height) +{ + GtkCellAreaBoxContextPrivate *priv; + GArray *group_array; + + g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context)); + + priv = box_context->priv; + g_return_if_fail (group_idx < priv->base_widths->len); + + group_array = g_hash_table_lookup (priv->heights, GINT_TO_POINTER (for_width)); + + if (group_array) + { + CachedSize *size = &g_array_index (group_array, CachedSize, group_idx); + + if (minimum_height) + *minimum_height = size->min_size; + + if (natural_height) + *natural_height = size->nat_size; + } + else + { + if (minimum_height) + *minimum_height = -1; + + if (natural_height) + *natural_height = -1; + } +} + +void +_gtk_cell_area_box_context_get_group_height (GtkCellAreaBoxContext *box_context, + int group_idx, + int *minimum_height, + int *natural_height) +{ + GtkCellAreaBoxContextPrivate *priv; + CachedSize *size; + + g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context)); + + priv = box_context->priv; + g_return_if_fail (group_idx < priv->base_heights->len); + + size = &g_array_index (priv->base_heights, CachedSize, group_idx); + + if (minimum_height) + *minimum_height = size->min_size; + + if (natural_height) + *natural_height = size->nat_size; +} + +void +_gtk_cell_area_box_context_get_group_width_for_height (GtkCellAreaBoxContext *box_context, + int group_idx, + int for_height, + int *minimum_width, + int *natural_width) +{ + GtkCellAreaBoxContextPrivate *priv; + GArray *group_array; + + g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context)); + + priv = box_context->priv; + g_return_if_fail (group_idx < priv->base_widths->len); + + group_array = g_hash_table_lookup (priv->widths, GINT_TO_POINTER (for_height)); + + if (group_array) + { + CachedSize *size = &g_array_index (group_array, CachedSize, group_idx); + + if (minimum_width) + *minimum_width = size->min_size; + + if (natural_width) + *natural_width = size->nat_size; + } + else + { + if (minimum_width) + *minimum_width = -1; + + if (natural_width) + *natural_width = -1; + } +} + +static GtkRequestedSize * +_gtk_cell_area_box_context_get_requests (GtkCellAreaBoxContext *box_context, + GtkCellAreaBox *area, + GtkOrientation orientation, + int for_size, + int *n_requests) +{ + GtkCellAreaBoxContextPrivate *priv = box_context->priv; + GtkRequestedSize *requests; + GArray *array; + CachedSize *size; + int visible_groups = 0; + int last_aligned_group_idx = 0; + int i, j; + + /* Get the last visible aligned group + * (we need to get space at least up till this group) */ + for (i = priv->base_widths->len - 1; i >= 0; i--) + { + if (priv->align[i] && + _gtk_cell_area_box_group_visible (area, i)) + break; + } + last_aligned_group_idx = i >= 0 ? i : 0; + + priv = box_context->priv; + array = get_array (box_context, orientation, for_size); + + for (i = 0; i < array->len; i++) + { + size = &g_array_index (array, CachedSize, i); + + if (size->nat_size > 0 && + (i <= last_aligned_group_idx || + _gtk_cell_area_box_group_visible (area, i))) + visible_groups++; + } + + requests = g_new (GtkRequestedSize, visible_groups); + + for (j = 0, i = 0; i < array->len; i++) + { + size = &g_array_index (array, CachedSize, i); + + if (size->nat_size > 0 && + (i <= last_aligned_group_idx || + _gtk_cell_area_box_group_visible (area, i))) + { + requests[j].data = GINT_TO_POINTER (i); + requests[j].minimum_size = size->min_size; + requests[j].natural_size = size->nat_size; + j++; + } + } + + if (n_requests) + *n_requests = visible_groups; + + return requests; +} + +static GtkCellAreaBoxAllocation * +allocate_for_orientation (GtkCellAreaBoxContext *context, + GtkCellAreaBox *area, + GtkOrientation orientation, + int spacing, + int size, + int for_size, + int *n_allocs) +{ + GtkCellAreaBoxContextPrivate *priv = context->priv; + GtkCellAreaBoxAllocation *allocs; + GtkRequestedSize *sizes; + int n_expand_groups = 0; + int i, n_groups, position, vis_position; + int extra_size, extra_extra; + int avail_size = size; + + sizes = _gtk_cell_area_box_context_get_requests (context, area, orientation, for_size, &n_groups); + n_expand_groups = count_expand_groups (context); + + /* First start by naturally allocating space among groups */ + avail_size -= (n_groups - 1) * spacing; + for (i = 0; i < n_groups; i++) + avail_size -= sizes[i].minimum_size; + + if (avail_size > 0) + avail_size = gtk_distribute_natural_allocation (avail_size, n_groups, sizes); + else + avail_size = 0; + + /* Calculate/distribute expand for groups */ + if (n_expand_groups > 0) + { + extra_size = avail_size / n_expand_groups; + extra_extra = avail_size % n_expand_groups; + } + else + extra_size = extra_extra = 0; + + allocs = g_new (GtkCellAreaBoxAllocation, n_groups); + + for (vis_position = 0, position = 0, i = 0; i < n_groups; i++) + { + allocs[i].group_idx = GPOINTER_TO_INT (sizes[i].data); + + if (priv->align[allocs[i].group_idx]) + vis_position = position; + + allocs[i].position = vis_position; + allocs[i].size = sizes[i].minimum_size; + + if (group_expands (context, allocs[i].group_idx)) + { + allocs[i].size += extra_size; + if (extra_extra) + { + allocs[i].size++; + extra_extra--; + } + } + + position += allocs[i].size; + position += spacing; + + if (_gtk_cell_area_box_group_visible (area, allocs[i].group_idx)) + { + vis_position += allocs[i].size; + vis_position += spacing; + } + } + + if (n_allocs) + *n_allocs = n_groups; + + g_free (sizes); + + return allocs; +} + +GtkRequestedSize * +_gtk_cell_area_box_context_get_widths (GtkCellAreaBoxContext *box_context, + int *n_widths) +{ + GtkCellAreaBox *area = (GtkCellAreaBox *)gtk_cell_area_context_get_area (GTK_CELL_AREA_CONTEXT (box_context)); + + return _gtk_cell_area_box_context_get_requests (box_context, area, GTK_ORIENTATION_HORIZONTAL, -1, n_widths); +} + +GtkRequestedSize * +_gtk_cell_area_box_context_get_heights (GtkCellAreaBoxContext *box_context, + int *n_heights) +{ + GtkCellAreaBox *area = (GtkCellAreaBox *)gtk_cell_area_context_get_area (GTK_CELL_AREA_CONTEXT (box_context)); + + return _gtk_cell_area_box_context_get_requests (box_context, area, GTK_ORIENTATION_VERTICAL, -1, n_heights); +} + +GtkCellAreaBoxAllocation * +_gtk_cell_area_box_context_get_orientation_allocs (GtkCellAreaBoxContext *context, + int *n_allocs) +{ + GtkCellAreaContext *ctx = GTK_CELL_AREA_CONTEXT (context); + GtkCellAreaBox *area; + GtkOrientation orientation; + int spacing, width, height, alloc_count = 0; + GtkCellAreaBoxAllocation *allocs = NULL; + + area = (GtkCellAreaBox *)gtk_cell_area_context_get_area (ctx); + orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (area)); + spacing = gtk_cell_area_box_get_spacing (area); + + gtk_cell_area_context_get_allocation (ctx, &width, &height); + + if (orientation == GTK_ORIENTATION_HORIZONTAL && width > 0) + allocs = allocate_for_orientation (context, area, orientation, + spacing, width, height, + &alloc_count); + else if (orientation == GTK_ORIENTATION_VERTICAL && height > 0) + allocs = allocate_for_orientation (context, area, orientation, + spacing, height, width, + &alloc_count); + + *n_allocs = alloc_count; + + return allocs; +} diff --git a/gtk/deprecated/gtkcellareaboxcontextprivate.h b/gtk/deprecated/gtkcellareaboxcontextprivate.h new file mode 100644 index 0000000000..aff5e22689 --- /dev/null +++ b/gtk/deprecated/gtkcellareaboxcontextprivate.h @@ -0,0 +1,137 @@ +/* gtkcellareaboxcontext.h + * + * Copyright (C) 2010 Openismus GmbH + * + * Authors: + * Tristan Van Berkom + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#ifndef __GTK_CELL_AREA_BOX_CONTEXT_H__ +#define __GTK_CELL_AREA_BOX_CONTEXT_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include +#include +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_CELL_AREA_BOX_CONTEXT (_gtk_cell_area_box_context_get_type ()) +#define GTK_CELL_AREA_BOX_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_AREA_BOX_CONTEXT, GtkCellAreaBoxContext)) +#define GTK_CELL_AREA_BOX_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_CELL_AREA_BOX_CONTEXT, GtkCellAreaBoxContextClass)) +#define GTK_IS_CELL_AREA_BOX_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_AREA_BOX_CONTEXT)) +#define GTK_IS_CELL_AREA_BOX_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_CELL_AREA_BOX_CONTEXT)) +#define GTK_CELL_AREA_BOX_CONTEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CELL_AREA_BOX_CONTEXT, GtkCellAreaBoxContextClass)) + +typedef struct _GtkCellAreaBoxContext GtkCellAreaBoxContext; +typedef struct _GtkCellAreaBoxContextClass GtkCellAreaBoxContextClass; +typedef struct _GtkCellAreaBoxContextPrivate GtkCellAreaBoxContextPrivate; + +struct _GtkCellAreaBoxContext +{ + GtkCellAreaContext parent_instance; + + GtkCellAreaBoxContextPrivate *priv; +}; + +struct _GtkCellAreaBoxContextClass +{ + GtkCellAreaContextClass parent_class; + +}; + +GType _gtk_cell_area_box_context_get_type (void) G_GNUC_CONST; + + +/* Create a duplicate of the context */ +GtkCellAreaBoxContext *_gtk_cell_area_box_context_copy (GtkCellAreaBox *box, + GtkCellAreaBoxContext *box_context); + +/* Initialize group array dimensions */ +void _gtk_cell_area_box_init_groups (GtkCellAreaBoxContext *box_context, + guint n_groups, + gboolean *expand_groups, + gboolean *align_groups); + +/* Update cell-group sizes */ +void _gtk_cell_area_box_context_push_group_width (GtkCellAreaBoxContext *box_context, + int group_idx, + int minimum_width, + int natural_width); + +void _gtk_cell_area_box_context_push_group_height_for_width (GtkCellAreaBoxContext *box_context, + int group_idx, + int for_width, + int minimum_height, + int natural_height); + +void _gtk_cell_area_box_context_push_group_height (GtkCellAreaBoxContext *box_context, + int group_idx, + int minimum_height, + int natural_height); + +void _gtk_cell_area_box_context_push_group_width_for_height (GtkCellAreaBoxContext *box_context, + int group_idx, + int for_height, + int minimum_width, + int natural_width); + +/* Fetch cell-group sizes */ +void _gtk_cell_area_box_context_get_group_width (GtkCellAreaBoxContext *box_context, + int group_idx, + int *minimum_width, + int *natural_width); + +void _gtk_cell_area_box_context_get_group_height_for_width (GtkCellAreaBoxContext *box_context, + int group_idx, + int for_width, + int *minimum_height, + int *natural_height); + +void _gtk_cell_area_box_context_get_group_height (GtkCellAreaBoxContext *box_context, + int group_idx, + int *minimum_height, + int *natural_height); + +void _gtk_cell_area_box_context_get_group_width_for_height (GtkCellAreaBoxContext *box_context, + int group_idx, + int for_height, + int *minimum_width, + int *natural_width); + +GtkRequestedSize *_gtk_cell_area_box_context_get_widths (GtkCellAreaBoxContext *box_context, + int *n_widths); +GtkRequestedSize *_gtk_cell_area_box_context_get_heights (GtkCellAreaBoxContext *box_context, + int *n_heights); + +/* Private context/area interaction */ +typedef struct { + int group_idx; /* Groups containing only invisible cells are not allocated */ + int position; /* Relative group allocation position in the orientation of the box */ + int size; /* Full allocated size of the cells in this group spacing inclusive */ +} GtkCellAreaBoxAllocation; + +GtkCellAreaBoxAllocation * +_gtk_cell_area_box_context_get_orientation_allocs (GtkCellAreaBoxContext *context, + int *n_allocs); + +G_END_DECLS + +#endif /* __GTK_CELL_AREA_BOX_CONTEXT_H__ */ diff --git a/gtk/deprecated/gtkcellareacontext.c b/gtk/deprecated/gtkcellareacontext.c new file mode 100644 index 0000000000..e16680888a --- /dev/null +++ b/gtk/deprecated/gtkcellareacontext.c @@ -0,0 +1,628 @@ +/* gtkcellareacontext.c + * + * Copyright (C) 2010 Openismus GmbH + * + * Authors: + * Tristan Van Berkom + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +/** + * GtkCellAreaContext: + * + * Stores geometrical information for a series of rows in a GtkCellArea + * + * The `GtkCellAreaContext` object is created by a given `GtkCellArea` + * implementation via its `GtkCellAreaClass.create_context()` virtual + * method and is used to store cell sizes and alignments for a series of + * `GtkTreeModel` rows that are requested and rendered in the same context. + * + * `GtkCellLayout` widgets can create any number of contexts in which to + * request and render groups of data rows. However, it’s important that the + * same context which was used to request sizes for a given `GtkTreeModel` + * row also be used for the same row when calling other `GtkCellArea` APIs + * such as gtk_cell_area_render() and gtk_cell_area_event(). + */ + +#include "config.h" +#include "gtkmarshalers.h" +#include "gtkcellareacontext.h" +#include "gtkprivate.h" + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + +/* GObjectClass */ +static void gtk_cell_area_context_dispose (GObject *object); +static void gtk_cell_area_context_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static void gtk_cell_area_context_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); + +/* GtkCellAreaContextClass */ +static void gtk_cell_area_context_real_reset (GtkCellAreaContext *context); +static void gtk_cell_area_context_real_allocate (GtkCellAreaContext *context, + int width, + int height); + +typedef struct _GtkCellAreaContextPrivate GtkCellAreaContextPrivate; +struct _GtkCellAreaContextPrivate +{ + GtkCellArea *cell_area; + + int min_width; + int nat_width; + int min_height; + int nat_height; + int alloc_width; + int alloc_height; +}; + +enum { + PROP_0, + PROP_CELL_AREA, + PROP_MIN_WIDTH, + PROP_NAT_WIDTH, + PROP_MIN_HEIGHT, + PROP_NAT_HEIGHT +}; + +G_DEFINE_TYPE_WITH_PRIVATE (GtkCellAreaContext, gtk_cell_area_context, G_TYPE_OBJECT) + +static void +gtk_cell_area_context_init (GtkCellAreaContext *context) +{ +} + +static void +gtk_cell_area_context_class_init (GtkCellAreaContextClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + /* GObjectClass */ + object_class->dispose = gtk_cell_area_context_dispose; + object_class->get_property = gtk_cell_area_context_get_property; + object_class->set_property = gtk_cell_area_context_set_property; + + /* GtkCellAreaContextClass */ + class->reset = gtk_cell_area_context_real_reset; + class->allocate = gtk_cell_area_context_real_allocate; + + /** + * GtkCellAreaContext:area: + * + * The `GtkCellArea` this context was created by + */ + g_object_class_install_property (object_class, + PROP_CELL_AREA, + g_param_spec_object ("area", NULL, NULL, + GTK_TYPE_CELL_AREA, + GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + /** + * GtkCellAreaContext:minimum-width: + * + * The minimum width for the `GtkCellArea` in this context + * for all `GtkTreeModel` rows that this context was requested + * for using gtk_cell_area_get_preferred_width(). + */ + g_object_class_install_property (object_class, + PROP_MIN_WIDTH, + g_param_spec_int ("minimum-width", NULL, NULL, + -1, G_MAXINT, -1, + GTK_PARAM_READABLE)); + + /** + * GtkCellAreaContext:natural-width: + * + * The natural width for the `GtkCellArea` in this context + * for all `GtkTreeModel` rows that this context was requested + * for using gtk_cell_area_get_preferred_width(). + */ + g_object_class_install_property (object_class, + PROP_NAT_WIDTH, + g_param_spec_int ("natural-width", NULL, NULL, + -1, G_MAXINT, -1, + GTK_PARAM_READABLE)); + + /** + * GtkCellAreaContext:minimum-height: + * + * The minimum height for the `GtkCellArea` in this context + * for all `GtkTreeModel` rows that this context was requested + * for using gtk_cell_area_get_preferred_height(). + */ + g_object_class_install_property (object_class, + PROP_MIN_HEIGHT, + g_param_spec_int ("minimum-height", NULL, NULL, + -1, G_MAXINT, -1, + GTK_PARAM_READABLE)); + + /** + * GtkCellAreaContext:natural-height: + * + * The natural height for the `GtkCellArea` in this context + * for all `GtkTreeModel` rows that this context was requested + * for using gtk_cell_area_get_preferred_height(). + */ + g_object_class_install_property (object_class, + PROP_NAT_HEIGHT, + g_param_spec_int ("natural-height", NULL, NULL, + -1, G_MAXINT, -1, + GTK_PARAM_READABLE)); +} + +/************************************************************* + * GObjectClass * + *************************************************************/ +static void +gtk_cell_area_context_dispose (GObject *object) +{ + GtkCellAreaContext *context = GTK_CELL_AREA_CONTEXT (object); + GtkCellAreaContextPrivate *priv = gtk_cell_area_context_get_instance_private (context); + + if (priv->cell_area) + { + g_object_unref (priv->cell_area); + + priv->cell_area = NULL; + } + + G_OBJECT_CLASS (gtk_cell_area_context_parent_class)->dispose (object); +} + +static void +gtk_cell_area_context_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkCellAreaContext *context = GTK_CELL_AREA_CONTEXT (object); + GtkCellAreaContextPrivate *priv = gtk_cell_area_context_get_instance_private (context); + + switch (prop_id) + { + case PROP_CELL_AREA: + priv->cell_area = g_value_dup_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_cell_area_context_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkCellAreaContext *context = GTK_CELL_AREA_CONTEXT (object); + GtkCellAreaContextPrivate *priv = gtk_cell_area_context_get_instance_private (context); + + switch (prop_id) + { + case PROP_CELL_AREA: + g_value_set_object (value, priv->cell_area); + break; + case PROP_MIN_WIDTH: + g_value_set_int (value, priv->min_width); + break; + case PROP_NAT_WIDTH: + g_value_set_int (value, priv->nat_width); + break; + case PROP_MIN_HEIGHT: + g_value_set_int (value, priv->min_height); + break; + case PROP_NAT_HEIGHT: + g_value_set_int (value, priv->nat_height); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +/************************************************************* + * GtkCellAreaContextClass * + *************************************************************/ +static void +gtk_cell_area_context_real_reset (GtkCellAreaContext *context) +{ + GtkCellAreaContextPrivate *priv = gtk_cell_area_context_get_instance_private (context); + + g_object_freeze_notify (G_OBJECT (context)); + + if (priv->min_width != 0) + { + priv->min_width = 0; + g_object_notify (G_OBJECT (context), "minimum-width"); + } + + if (priv->nat_width != 0) + { + priv->nat_width = 0; + g_object_notify (G_OBJECT (context), "natural-width"); + } + + if (priv->min_height != 0) + { + priv->min_height = 0; + g_object_notify (G_OBJECT (context), "minimum-height"); + } + + if (priv->nat_height != 0) + { + priv->nat_height = 0; + g_object_notify (G_OBJECT (context), "natural-height"); + } + + priv->alloc_width = 0; + priv->alloc_height = 0; + + g_object_thaw_notify (G_OBJECT (context)); +} + +static void +gtk_cell_area_context_real_allocate (GtkCellAreaContext *context, + int width, + int height) +{ + GtkCellAreaContextPrivate *priv = gtk_cell_area_context_get_instance_private (context); + + priv->alloc_width = width; + priv->alloc_height = height; +} + +/************************************************************* + * API * + *************************************************************/ +/** + * gtk_cell_area_context_get_area: + * @context: a `GtkCellAreaContext` + * + * Fetches the `GtkCellArea` this @context was created by. + * + * This is generally unneeded by layouting widgets; however, + * it is important for the context implementation itself to + * fetch information about the area it is being used for. + * + * For instance at `GtkCellAreaContextClass.allocate()` time + * it’s important to know details about any cell spacing + * that the `GtkCellArea` is configured with in order to + * compute a proper allocation. + * + * Returns: (transfer none): the `GtkCellArea` this context was created by. + * + * Deprecated: 4.10 + */ +GtkCellArea * +gtk_cell_area_context_get_area (GtkCellAreaContext *context) +{ + GtkCellAreaContextPrivate *priv = gtk_cell_area_context_get_instance_private (context); + + g_return_val_if_fail (GTK_IS_CELL_AREA_CONTEXT (context), NULL); + + return priv->cell_area; +} + +/** + * gtk_cell_area_context_reset: + * @context: a `GtkCellAreaContext` + * + * Resets any previously cached request and allocation + * data. + * + * When underlying `GtkTreeModel` data changes its + * important to reset the context if the content + * size is allowed to shrink. If the content size + * is only allowed to grow (this is usually an option + * for views rendering large data stores as a measure + * of optimization), then only the row that changed + * or was inserted needs to be (re)requested with + * gtk_cell_area_get_preferred_width(). + * + * When the new overall size of the context requires + * that the allocated size changes (or whenever this + * allocation changes at all), the variable row + * sizes need to be re-requested for every row. + * + * For instance, if the rows are displayed all with + * the same width from top to bottom then a change + * in the allocated width necessitates a recalculation + * of all the displayed row heights using + * gtk_cell_area_get_preferred_height_for_width(). + * + * Deprecated: 4.10 + */ +void +gtk_cell_area_context_reset (GtkCellAreaContext *context) +{ + g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context)); + + GTK_CELL_AREA_CONTEXT_GET_CLASS (context)->reset (context); +} + +/** + * gtk_cell_area_context_allocate: + * @context: a `GtkCellAreaContext` + * @width: the allocated width for all `GtkTreeModel` rows rendered + * with @context, or -1 + * @height: the allocated height for all `GtkTreeModel` rows rendered + * with @context, or -1 + * + * Allocates a width and/or a height for all rows which are to be + * rendered with @context. + * + * Usually allocation is performed only horizontally or sometimes + * vertically since a group of rows are usually rendered side by + * side vertically or horizontally and share either the same width + * or the same height. Sometimes they are allocated in both horizontal + * and vertical orientations producing a homogeneous effect of the + * rows. This is generally the case for `GtkTreeView` when + * `GtkTreeView:fixed-height-mode` is enabled. + * + * Deprecated: 4.10 + */ +void +gtk_cell_area_context_allocate (GtkCellAreaContext *context, + int width, + int height) +{ + g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context)); + + GTK_CELL_AREA_CONTEXT_GET_CLASS (context)->allocate (context, width, height); +} + +/** + * gtk_cell_area_context_get_preferred_width: + * @context: a `GtkCellAreaContext` + * @minimum_width: (out) (optional): location to store the minimum width + * @natural_width: (out) (optional): location to store the natural width + * + * Gets the accumulative preferred width for all rows which have been + * requested with this context. + * + * After gtk_cell_area_context_reset() is called and/or before ever + * requesting the size of a `GtkCellArea`, the returned values are 0. + * + * Deprecated: 4.10 + */ +void +gtk_cell_area_context_get_preferred_width (GtkCellAreaContext *context, + int *minimum_width, + int *natural_width) +{ + GtkCellAreaContextPrivate *priv = gtk_cell_area_context_get_instance_private (context); + + g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context)); + + if (minimum_width) + *minimum_width = priv->min_width; + + if (natural_width) + *natural_width = priv->nat_width; +} + +/** + * gtk_cell_area_context_get_preferred_height: + * @context: a `GtkCellAreaContext` + * @minimum_height: (out) (optional): location to store the minimum height + * @natural_height: (out) (optional): location to store the natural height + * + * Gets the accumulative preferred height for all rows which have been + * requested with this context. + * + * After gtk_cell_area_context_reset() is called and/or before ever + * requesting the size of a `GtkCellArea`, the returned values are 0. + * + * Deprecated: 4.10 + */ +void +gtk_cell_area_context_get_preferred_height (GtkCellAreaContext *context, + int *minimum_height, + int *natural_height) +{ + GtkCellAreaContextPrivate *priv = gtk_cell_area_context_get_instance_private (context); + + g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context)); + + if (minimum_height) + *minimum_height = priv->min_height; + + if (natural_height) + *natural_height = priv->nat_height; +} + +/** + * gtk_cell_area_context_get_preferred_height_for_width: + * @context: a `GtkCellAreaContext` + * @width: a proposed width for allocation + * @minimum_height: (out) (optional): location to store the minimum height + * @natural_height: (out) (optional): location to store the natural height + * + * Gets the accumulative preferred height for @width for all rows + * which have been requested for the same said @width with this context. + * + * After gtk_cell_area_context_reset() is called and/or before ever + * requesting the size of a `GtkCellArea`, the returned values are -1. + * + * Deprecated: 4.10 + */ +void +gtk_cell_area_context_get_preferred_height_for_width (GtkCellAreaContext *context, + int width, + int *minimum_height, + int *natural_height) +{ + g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context)); + + if (GTK_CELL_AREA_CONTEXT_GET_CLASS (context)->get_preferred_height_for_width) + GTK_CELL_AREA_CONTEXT_GET_CLASS (context)->get_preferred_height_for_width (context, + width, + minimum_height, + natural_height); +} + +/** + * gtk_cell_area_context_get_preferred_width_for_height: + * @context: a `GtkCellAreaContext` + * @height: a proposed height for allocation + * @minimum_width: (out) (optional): location to store the minimum width + * @natural_width: (out) (optional): location to store the natural width + * + * Gets the accumulative preferred width for @height for all rows which + * have been requested for the same said @height with this context. + * + * After gtk_cell_area_context_reset() is called and/or before ever + * requesting the size of a `GtkCellArea`, the returned values are -1. + * + * Deprecated: 4.10 + */ +void +gtk_cell_area_context_get_preferred_width_for_height (GtkCellAreaContext *context, + int height, + int *minimum_width, + int *natural_width) +{ + g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context)); + + if (GTK_CELL_AREA_CONTEXT_GET_CLASS (context)->get_preferred_width_for_height) + GTK_CELL_AREA_CONTEXT_GET_CLASS (context)->get_preferred_width_for_height (context, + height, + minimum_width, + natural_width); +} + +/** + * gtk_cell_area_context_get_allocation: + * @context: a `GtkCellAreaContext` + * @width: (out) (optional): location to store the allocated width + * @height: (out) (optional): location to store the allocated height + * + * Fetches the current allocation size for @context. + * + * If the context was not allocated in width or height, or if the + * context was recently reset with gtk_cell_area_context_reset(), + * the returned value will be -1. + * + * Deprecated: 4.10 + */ +void +gtk_cell_area_context_get_allocation (GtkCellAreaContext *context, + int *width, + int *height) +{ + GtkCellAreaContextPrivate *priv = gtk_cell_area_context_get_instance_private (context); + + g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context)); + + if (width) + *width = priv->alloc_width; + + if (height) + *height = priv->alloc_height; +} + +/** + * gtk_cell_area_context_push_preferred_width: + * @context: a `GtkCellAreaContext` + * @minimum_width: the proposed new minimum width for @context + * @natural_width: the proposed new natural width for @context + * + * Causes the minimum and/or natural width to grow if the new + * proposed sizes exceed the current minimum and natural width. + * + * This is used by `GtkCellAreaContext` implementations during + * the request process over a series of `GtkTreeModel` rows to + * progressively push the requested width over a series of + * gtk_cell_area_get_preferred_width() requests. + * + * Deprecated: 4.10 + */ +void +gtk_cell_area_context_push_preferred_width (GtkCellAreaContext *context, + int minimum_width, + int natural_width) +{ + GtkCellAreaContextPrivate *priv = gtk_cell_area_context_get_instance_private (context); + + g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context)); + + g_object_freeze_notify (G_OBJECT (context)); + + if (minimum_width > priv->min_width) + { + priv->min_width = minimum_width; + + g_object_notify (G_OBJECT (context), "minimum-width"); + } + + if (natural_width > priv->nat_width) + { + priv->nat_width = natural_width; + + g_object_notify (G_OBJECT (context), "natural-width"); + } + + g_object_thaw_notify (G_OBJECT (context)); +} + +/** + * gtk_cell_area_context_push_preferred_height: + * @context: a `GtkCellAreaContext` + * @minimum_height: the proposed new minimum height for @context + * @natural_height: the proposed new natural height for @context + * + * Causes the minimum and/or natural height to grow if the new + * proposed sizes exceed the current minimum and natural height. + * + * This is used by `GtkCellAreaContext` implementations during + * the request process over a series of `GtkTreeModel` rows to + * progressively push the requested height over a series of + * gtk_cell_area_get_preferred_height() requests. + * + * Deprecated: 4.10 + */ +void +gtk_cell_area_context_push_preferred_height (GtkCellAreaContext *context, + int minimum_height, + int natural_height) +{ + GtkCellAreaContextPrivate *priv = gtk_cell_area_context_get_instance_private (context); + + g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context)); + + g_object_freeze_notify (G_OBJECT (context)); + + if (minimum_height > priv->min_height) + { + priv->min_height = minimum_height; + + g_object_notify (G_OBJECT (context), "minimum-height"); + } + + if (natural_height > priv->nat_height) + { + priv->nat_height = natural_height; + + g_object_notify (G_OBJECT (context), "natural-height"); + } + + g_object_thaw_notify (G_OBJECT (context)); +} diff --git a/gtk/deprecated/gtkcellareacontext.h b/gtk/deprecated/gtkcellareacontext.h new file mode 100644 index 0000000000..c2d5a1b8a5 --- /dev/null +++ b/gtk/deprecated/gtkcellareacontext.h @@ -0,0 +1,141 @@ +/* gtkcellareacontext.h + * + * Copyright (C) 2010 Openismus GmbH + * + * Authors: + * Tristan Van Berkom + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#ifndef __GTK_CELL_AREA_CONTEXT_H__ +#define __GTK_CELL_AREA_CONTEXT_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_CELL_AREA_CONTEXT (gtk_cell_area_context_get_type ()) +#define GTK_CELL_AREA_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_AREA_CONTEXT, GtkCellAreaContext)) +#define GTK_CELL_AREA_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_CELL_AREA_CONTEXT, GtkCellAreaContextClass)) +#define GTK_IS_CELL_AREA_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_AREA_CONTEXT)) +#define GTK_IS_CELL_AREA_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_CELL_AREA_CONTEXT)) +#define GTK_CELL_AREA_CONTEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CELL_AREA_CONTEXT, GtkCellAreaContextClass)) + +typedef struct _GtkCellAreaContextPrivate GtkCellAreaContextPrivate; +typedef struct _GtkCellAreaContextClass GtkCellAreaContextClass; + +struct _GtkCellAreaContext +{ + /*< private >*/ + GObject parent_instance; +}; + +/** + * GtkCellAreaContextClass: + * @allocate: This tells the context that an allocation width or height + * (or both) have been decided for a group of rows. The context should + * store any allocations for internally aligned cells at this point so + * that they dont need to be recalculated at gtk_cell_area_render() time. + * @reset: Clear any previously stored information about requested and + * allocated sizes for the context. + * @get_preferred_height_for_width: Returns the aligned height for the given + * width that context must store while collecting sizes for it’s rows. + * @get_preferred_width_for_height: Returns the aligned width for the given + * height that context must store while collecting sizes for it’s rows. + */ +struct _GtkCellAreaContextClass +{ + /*< private >*/ + GObjectClass parent_class; + + /*< public >*/ + void (* allocate) (GtkCellAreaContext *context, + int width, + int height); + void (* reset) (GtkCellAreaContext *context); + void (* get_preferred_height_for_width) (GtkCellAreaContext *context, + int width, + int *minimum_height, + int *natural_height); + void (* get_preferred_width_for_height) (GtkCellAreaContext *context, + int height, + int *minimum_width, + int *natural_width); + + /*< private >*/ + + gpointer padding[8]; +}; + +GDK_AVAILABLE_IN_ALL +GType gtk_cell_area_context_get_type (void) G_GNUC_CONST; + +/* Main apis */ +GDK_DEPRECATED_IN_4_10 +GtkCellArea *gtk_cell_area_context_get_area (GtkCellAreaContext *context); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_area_context_allocate (GtkCellAreaContext *context, + int width, + int height); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_area_context_reset (GtkCellAreaContext *context); + +/* Apis for GtkCellArea clients to consult cached values + * for a series of GtkTreeModel rows + */ +GDK_DEPRECATED_IN_4_10 +void gtk_cell_area_context_get_preferred_width (GtkCellAreaContext *context, + int *minimum_width, + int *natural_width); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_area_context_get_preferred_height (GtkCellAreaContext *context, + int *minimum_height, + int *natural_height); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_area_context_get_preferred_height_for_width (GtkCellAreaContext *context, + int width, + int *minimum_height, + int *natural_height); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_area_context_get_preferred_width_for_height (GtkCellAreaContext *context, + int height, + int *minimum_width, + int *natural_width); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_area_context_get_allocation (GtkCellAreaContext *context, + int *width, + int *height); + +/* Apis for GtkCellArea implementations to update cached values + * for multiple GtkTreeModel rows + */ +GDK_DEPRECATED_IN_4_10 +void gtk_cell_area_context_push_preferred_width (GtkCellAreaContext *context, + int minimum_width, + int natural_width); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_area_context_push_preferred_height (GtkCellAreaContext *context, + int minimum_height, + int natural_height); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkCellAreaContext, g_object_unref) + +G_END_DECLS + +#endif /* __GTK_CELL_AREA_CONTEXT_H__ */ diff --git a/gtk/deprecated/gtkcelleditable.c b/gtk/deprecated/gtkcelleditable.c new file mode 100644 index 0000000000..719c328030 --- /dev/null +++ b/gtk/deprecated/gtkcelleditable.c @@ -0,0 +1,157 @@ +/* gtkcelleditable.c + * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +/** + * GtkCellEditable: + * + * Interface for widgets that can be used for editing cells + * + * The `GtkCellEditable` interface must be implemented for widgets to be usable + * to edit the contents of a `GtkTreeView` cell. It provides a way to specify how + * temporary widgets should be configured for editing, get the new value, etc. + */ + +#include "config.h" +#include "gtkcelleditable.h" +#include "gtkmarshalers.h" +#include "gtkprivate.h" + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + +typedef GtkCellEditableIface GtkCellEditableInterface; +G_DEFINE_INTERFACE(GtkCellEditable, gtk_cell_editable, GTK_TYPE_WIDGET) + +static void +gtk_cell_editable_default_init (GtkCellEditableInterface *iface) +{ + /** + * GtkCellEditable:editing-canceled: + * + * Indicates whether editing on the cell has been canceled. + */ + g_object_interface_install_property (iface, + g_param_spec_boolean ("editing-canceled", NULL, NULL, + FALSE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + /** + * GtkCellEditable::editing-done: + * @cell_editable: the object on which the signal was emitted + * + * This signal is a sign for the cell renderer to update its + * value from the @cell_editable. + * + * Implementations of `GtkCellEditable` are responsible for + * emitting this signal when they are done editing, e.g. + * `GtkEntry` emits this signal when the user presses Enter. Typical things to + * do in a handler for ::editing-done are to capture the edited value, + * disconnect the @cell_editable from signals on the `GtkCellRenderer`, etc. + * + * gtk_cell_editable_editing_done() is a convenience method + * for emitting `GtkCellEditable::editing-done`. + */ + g_signal_new (I_("editing-done"), + GTK_TYPE_CELL_EDITABLE, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkCellEditableIface, editing_done), + NULL, NULL, + NULL, + G_TYPE_NONE, 0); + + /** + * GtkCellEditable::remove-widget: + * @cell_editable: the object on which the signal was emitted + * + * This signal is meant to indicate that the cell is finished + * editing, and the @cell_editable widget is being removed and may + * subsequently be destroyed. + * + * Implementations of `GtkCellEditable` are responsible for + * emitting this signal when they are done editing. It must + * be emitted after the `GtkCellEditable::editing-done` signal, + * to give the cell renderer a chance to update the cell's value + * before the widget is removed. + * + * gtk_cell_editable_remove_widget() is a convenience method + * for emitting `GtkCellEditable::remove-widget`. + */ + g_signal_new (I_("remove-widget"), + GTK_TYPE_CELL_EDITABLE, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkCellEditableIface, remove_widget), + NULL, NULL, + NULL, + G_TYPE_NONE, 0); +} + +/** + * gtk_cell_editable_start_editing: + * @cell_editable: A `GtkCellEditable` + * @event: (nullable): The `GdkEvent` that began the editing process, or + * %NULL if editing was initiated programmatically + * + * Begins editing on a @cell_editable. + * + * The `GtkCellRenderer` for the cell creates and returns a `GtkCellEditable` from + * gtk_cell_renderer_start_editing(), configured for the `GtkCellRenderer` type. + * + * gtk_cell_editable_start_editing() can then set up @cell_editable suitably for + * editing a cell, e.g. making the Esc key emit `GtkCellEditable::editing-done`. + * + * Note that the @cell_editable is created on-demand for the current edit; its + * lifetime is temporary and does not persist across other edits and/or cells. + **/ +void +gtk_cell_editable_start_editing (GtkCellEditable *cell_editable, + GdkEvent *event) +{ + g_return_if_fail (GTK_IS_CELL_EDITABLE (cell_editable)); + + (* GTK_CELL_EDITABLE_GET_IFACE (cell_editable)->start_editing) (cell_editable, event); +} + +/** + * gtk_cell_editable_editing_done: + * @cell_editable: A `GtkCellEditable` + * + * Emits the `GtkCellEditable::editing-done` signal. + * + * Deprecated: 4.10 + */ +void +gtk_cell_editable_editing_done (GtkCellEditable *cell_editable) +{ + g_return_if_fail (GTK_IS_CELL_EDITABLE (cell_editable)); + + g_signal_emit_by_name (cell_editable, "editing-done"); +} + +/** + * gtk_cell_editable_remove_widget: + * @cell_editable: A `GtkCellEditable` + * + * Emits the `GtkCellEditable::remove-widget` signal. + * + * Deprecated: 4.10 + **/ +void +gtk_cell_editable_remove_widget (GtkCellEditable *cell_editable) +{ + g_return_if_fail (GTK_IS_CELL_EDITABLE (cell_editable)); + + g_signal_emit_by_name (cell_editable, "remove-widget"); +} diff --git a/gtk/deprecated/gtkcelleditable.h b/gtk/deprecated/gtkcelleditable.h new file mode 100644 index 0000000000..57db7cbe00 --- /dev/null +++ b/gtk/deprecated/gtkcelleditable.h @@ -0,0 +1,77 @@ +/* gtkcelleditable.h + * Copyright (C) 2001 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#ifndef __GTK_CELL_EDITABLE_H__ +#define __GTK_CELL_EDITABLE_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_CELL_EDITABLE (gtk_cell_editable_get_type ()) +#define GTK_CELL_EDITABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_EDITABLE, GtkCellEditable)) +#define GTK_IS_CELL_EDITABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_EDITABLE)) +#define GTK_CELL_EDITABLE_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GTK_TYPE_CELL_EDITABLE, GtkCellEditableIface)) + +typedef struct _GtkCellEditable GtkCellEditable; /* Dummy typedef */ +typedef struct _GtkCellEditableIface GtkCellEditableIface; + +/** + * GtkCellEditableIface: + * @editing_done: Signal is a sign for the cell renderer to update its + * value from the cell_editable. + * @remove_widget: Signal is meant to indicate that the cell is + * finished editing, and the widget may now be destroyed. + * @start_editing: Begins editing on a cell_editable. + */ +struct _GtkCellEditableIface +{ + /*< private >*/ + GTypeInterface g_iface; + + /*< public >*/ + + /* signals */ + void (* editing_done) (GtkCellEditable *cell_editable); + void (* remove_widget) (GtkCellEditable *cell_editable); + + /* virtual table */ + void (* start_editing) (GtkCellEditable *cell_editable, + GdkEvent *event); +}; + + +GDK_AVAILABLE_IN_ALL +GType gtk_cell_editable_get_type (void) G_GNUC_CONST; + +GDK_DEPRECATED_IN_4_10 +void gtk_cell_editable_start_editing (GtkCellEditable *cell_editable, + GdkEvent *event); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_editable_editing_done (GtkCellEditable *cell_editable); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_editable_remove_widget (GtkCellEditable *cell_editable); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkCellEditable, g_object_unref) + +G_END_DECLS + +#endif /* __GTK_CELL_EDITABLE_H__ */ diff --git a/gtk/deprecated/gtkcelllayout.c b/gtk/deprecated/gtkcelllayout.c new file mode 100644 index 0000000000..417e4b4e75 --- /dev/null +++ b/gtk/deprecated/gtkcelllayout.c @@ -0,0 +1,983 @@ +/* gtkcelllayout.c + * Copyright (C) 2003 Kristian Rietveld + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +/** + * GtkCellLayout: + * + * An interface for packing cells + * + * `GtkCellLayout` is an interface to be implemented by all objects which + * want to provide a `GtkTreeViewColumn` like API for packing cells, + * setting attributes and data funcs. + * + * One of the notable features provided by implementations of + * `GtkCellLayout` are attributes. Attributes let you set the properties + * in flexible ways. They can just be set to constant values like regular + * properties. But they can also be mapped to a column of the underlying + * tree model with gtk_cell_layout_set_attributes(), which means that the value + * of the attribute can change from cell to cell as they are rendered by + * the cell renderer. Finally, it is possible to specify a function with + * gtk_cell_layout_set_cell_data_func() that is called to determine the + * value of the attribute for each cell that is rendered. + * + * # GtkCellLayouts as GtkBuildable + * + * Implementations of GtkCellLayout which also implement the GtkBuildable + * interface (`GtkCellView`, `GtkIconView`, `GtkComboBox`, + * `GtkEntryCompletion`, `GtkTreeViewColumn`) accept `GtkCellRenderer` objects + * as `` elements in UI definitions. They support a custom `` + * element for their children, which can contain multiple `` + * elements. Each `` element has a name attribute which specifies + * a property of the cell renderer; the content of the element is the + * attribute value. + * + * This is an example of a UI definition fragment specifying attributes: + * + * ```xml + * + * + * + * + * 0 + * + * + * + * ``` + * + * Furthermore for implementations of `GtkCellLayout` that use a `GtkCellArea` + * to lay out cells (all `GtkCellLayout`s in GTK use a `GtkCellArea`) + * [cell properties](class.CellArea.html#cell-properties) can also be defined + * in the format by specifying the custom `` attribute which can + * contain multiple `` elements. + * + * Here is a UI definition fragment specifying cell properties: + * + * ```xml + * + * + * + * + * True + * False + * + * + * + * ``` + * + * # Subclassing GtkCellLayout implementations + * + * When subclassing a widget that implements `GtkCellLayout` like + * `GtkIconView` or `GtkComboBox`, there are some considerations related + * to the fact that these widgets internally use a `GtkCellArea`. + * The cell area is exposed as a construct-only property by these + * widgets. This means that it is possible to e.g. do + * + * ```c + * GtkWIdget *combo = + * g_object_new (GTK_TYPE_COMBO_BOX, "cell-area", my_cell_area, NULL); + * ``` + * + * to use a custom cell area with a combo box. But construct properties + * are only initialized after instance `init()` + * functions have run, which means that using functions which rely on + * the existence of the cell area in your subclass `init()` function will + * cause the default cell area to be instantiated. In this case, a provided + * construct property value will be ignored (with a warning, to alert + * you to the problem). + * + * ```c + * static void + * my_combo_box_init (MyComboBox *b) + * { + * GtkCellRenderer *cell; + * + * cell = gtk_cell_renderer_pixbuf_new (); + * + * // The following call causes the default cell area for combo boxes, + * // a GtkCellAreaBox, to be instantiated + * gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (b), cell, FALSE); + * ... + * } + * + * GtkWidget * + * my_combo_box_new (GtkCellArea *area) + * { + * // This call is going to cause a warning about area being ignored + * return g_object_new (MY_TYPE_COMBO_BOX, "cell-area", area, NULL); + * } + * ``` + * + * If supporting alternative cell areas with your derived widget is + * not important, then this does not have to concern you. If you want + * to support alternative cell areas, you can do so by moving the + * problematic calls out of `init()` and into a `constructor()` + * for your class. + */ + +#include "config.h" +#include +#include +#include +#include "gtkcelllayout.h" +#include "gtkbuilderprivate.h" + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + +#define warn_no_cell_area(func) \ + g_critical ("%s: Called but no GtkCellArea is available yet", func) + +typedef GtkCellLayoutIface GtkCellLayoutInterface; +G_DEFINE_INTERFACE (GtkCellLayout, gtk_cell_layout, G_TYPE_OBJECT); + +static void gtk_cell_layout_default_pack_start (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + gboolean expand); +static void gtk_cell_layout_default_pack_end (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + gboolean expand); +static void gtk_cell_layout_default_clear (GtkCellLayout *cell_layout); +static void gtk_cell_layout_default_add_attribute (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + const char *attribute, + int column); +static void gtk_cell_layout_default_set_cell_data_func (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + GtkCellLayoutDataFunc func, + gpointer func_data, + GDestroyNotify destroy); +static void gtk_cell_layout_default_clear_attributes (GtkCellLayout *cell_layout, + GtkCellRenderer *cell); +static void gtk_cell_layout_default_reorder (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + int position); +static GList *gtk_cell_layout_default_get_cells (GtkCellLayout *cell_layout); + + +static void +gtk_cell_layout_default_init (GtkCellLayoutIface *iface) +{ + iface->pack_start = gtk_cell_layout_default_pack_start; + iface->pack_end = gtk_cell_layout_default_pack_end; + iface->clear = gtk_cell_layout_default_clear; + iface->add_attribute = gtk_cell_layout_default_add_attribute; + iface->set_cell_data_func = gtk_cell_layout_default_set_cell_data_func; + iface->clear_attributes = gtk_cell_layout_default_clear_attributes; + iface->reorder = gtk_cell_layout_default_reorder; + iface->get_cells = gtk_cell_layout_default_get_cells; +} + +/* Default implementation is to fall back on an underlying cell area */ +static void +gtk_cell_layout_default_pack_start (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + gboolean expand) +{ + GtkCellLayoutIface *iface; + GtkCellArea *area; + + iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout); + + if (iface->get_area) + { + area = iface->get_area (cell_layout); + + if (area) + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (area), cell, expand); + else + warn_no_cell_area ("GtkCellLayoutIface->pack_start()"); + } +} + +static void +gtk_cell_layout_default_pack_end (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + gboolean expand) +{ + GtkCellLayoutIface *iface; + GtkCellArea *area; + + iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout); + + if (iface->get_area) + { + area = iface->get_area (cell_layout); + + if (area) + gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (area), cell, expand); + else + warn_no_cell_area ("GtkCellLayoutIface->pack_end()"); + } +} + +static void +gtk_cell_layout_default_clear (GtkCellLayout *cell_layout) +{ + GtkCellLayoutIface *iface; + GtkCellArea *area; + + iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout); + + if (iface->get_area) + { + area = iface->get_area (cell_layout); + + if (area) + gtk_cell_layout_clear (GTK_CELL_LAYOUT (area)); + else + warn_no_cell_area ("GtkCellLayoutIface->clear()"); + } +} + +static void +gtk_cell_layout_default_add_attribute (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + const char *attribute, + int column) +{ + GtkCellLayoutIface *iface; + GtkCellArea *area; + + iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout); + + if (iface->get_area) + { + area = iface->get_area (cell_layout); + + if (area) + gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (area), cell, attribute, column); + else + warn_no_cell_area ("GtkCellLayoutIface->add_attribute()"); + } +} + +static void +gtk_cell_layout_default_set_cell_data_func (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + GtkCellLayoutDataFunc func, + gpointer func_data, + GDestroyNotify destroy) +{ + GtkCellLayoutIface *iface; + GtkCellArea *area; + + iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout); + + if (iface->get_area) + { + area = iface->get_area (cell_layout); + + if (area) + _gtk_cell_area_set_cell_data_func_with_proxy (area, cell, + (GFunc)func, func_data, destroy, + cell_layout); + else + warn_no_cell_area ("GtkCellLayoutIface->set_cell_data_func()"); + } +} + +static void +gtk_cell_layout_default_clear_attributes (GtkCellLayout *cell_layout, + GtkCellRenderer *cell) +{ + GtkCellLayoutIface *iface; + GtkCellArea *area; + + iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout); + + if (iface->get_area) + { + area = iface->get_area (cell_layout); + + if (area) + gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (area), cell); + else + warn_no_cell_area ("GtkCellLayoutIface->clear_attributes()"); + } +} + +static void +gtk_cell_layout_default_reorder (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + int position) +{ + GtkCellLayoutIface *iface; + GtkCellArea *area; + + iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout); + + if (iface->get_area) + { + area = iface->get_area (cell_layout); + + if (area) + gtk_cell_layout_reorder (GTK_CELL_LAYOUT (area), cell, position); + else + warn_no_cell_area ("GtkCellLayoutIface->reorder()"); + } +} + +static GList * +gtk_cell_layout_default_get_cells (GtkCellLayout *cell_layout) +{ + GtkCellLayoutIface *iface; + GtkCellArea *area; + + iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout); + + if (iface->get_area) + { + area = iface->get_area (cell_layout); + + if (area) + return gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (area)); + else + warn_no_cell_area ("GtkCellLayoutIface->get_cells()"); + } + return NULL; +} + + +/** + * gtk_cell_layout_pack_start: + * @cell_layout: a `GtkCellLayout` + * @cell: a `GtkCellRenderer` + * @expand: %TRUE if @cell is to be given extra space allocated to @cell_layout + * + * Packs the @cell into the beginning of @cell_layout. If @expand is %FALSE, + * then the @cell is allocated no more space than it needs. Any unused space + * is divided evenly between cells for which @expand is %TRUE. + * + * Note that reusing the same cell renderer is not supported. + * + * Deprecated: 4.10 + */ +void +gtk_cell_layout_pack_start (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + gboolean expand) +{ + g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout)); + g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); + + GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->pack_start (cell_layout, cell, expand); +} + +/** + * gtk_cell_layout_pack_end: + * @cell_layout: a `GtkCellLayout` + * @cell: a `GtkCellRenderer` + * @expand: %TRUE if @cell is to be given extra space allocated to @cell_layout + * + * Adds the @cell to the end of @cell_layout. If @expand is %FALSE, then the + * @cell is allocated no more space than it needs. Any unused space is + * divided evenly between cells for which @expand is %TRUE. + * + * Note that reusing the same cell renderer is not supported. + * + * Deprecated: 4.10 + */ +void +gtk_cell_layout_pack_end (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + gboolean expand) +{ + g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout)); + g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); + + GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->pack_end (cell_layout, cell, expand); +} + +/** + * gtk_cell_layout_clear: + * @cell_layout: a `GtkCellLayout` + * + * Unsets all the mappings on all renderers on @cell_layout and + * removes all renderers from @cell_layout. + * + * Deprecated: 4.10 + */ +void +gtk_cell_layout_clear (GtkCellLayout *cell_layout) +{ + g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout)); + + GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->clear (cell_layout); +} + +static void +gtk_cell_layout_set_attributesv (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + va_list args) +{ + char *attribute; + int column; + + attribute = va_arg (args, char *); + + gtk_cell_layout_clear_attributes (cell_layout, cell); + + while (attribute != NULL) + { + column = va_arg (args, int); + + gtk_cell_layout_add_attribute (cell_layout, cell, attribute, column); + + attribute = va_arg (args, char *); + } +} + +/** + * gtk_cell_layout_set_attributes: + * @cell_layout: a `GtkCellLayout` + * @cell: a `GtkCellRenderer` + * @...: a %NULL-terminated list of attributes + * + * Sets the attributes in the parameter list as the attributes + * of @cell_layout. + * + * See [method@Gtk.CellLayout.add_attribute] for more details. + * + * The attributes should be in attribute/column order, as in + * gtk_cell_layout_add_attribute(). All existing attributes are + * removed, and replaced with the new attributes. + * + * Deprecated: 4.10 + */ +void +gtk_cell_layout_set_attributes (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + ...) +{ + va_list args; + + g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout)); + g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); + + va_start (args, cell); + gtk_cell_layout_set_attributesv (cell_layout, cell, args); + va_end (args); +} + +/** + * gtk_cell_layout_add_attribute: + * @cell_layout: a `GtkCellLayout` + * @cell: a `GtkCellRenderer` + * @attribute: a property on the renderer + * @column: the column position on the model to get the attribute from + * + * Adds an attribute mapping to the list in @cell_layout. + * + * The @column is the column of the model to get a value from, and the + * @attribute is the property on @cell to be set from that value. So for + * example if column 2 of the model contains strings, you could have the + * “text” attribute of a `GtkCellRendererText` get its values from column 2. + * In this context "attribute" and "property" are used interchangeably. + * + * Deprecated: 4.10 + */ +void +gtk_cell_layout_add_attribute (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + const char *attribute, + int column) +{ + g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout)); + g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); + g_return_if_fail (attribute != NULL); + g_return_if_fail (column >= 0); + + GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->add_attribute (cell_layout, cell, attribute, column); +} + +/** + * gtk_cell_layout_set_cell_data_func: + * @cell_layout: a `GtkCellLayout` + * @cell: a `GtkCellRenderer` + * @func: (nullable): the `GtkCellLayout`DataFunc to use + * @func_data: (closure): user data for @func + * @destroy: destroy notify for @func_data + * + * Sets the `GtkCellLayout`DataFunc to use for @cell_layout. + * + * This function is used instead of the standard attributes mapping + * for setting the column value, and should set the value of @cell_layout’s + * cell renderer(s) as appropriate. + * + * @func may be %NULL to remove a previously set function. + * + * Deprecated: 4.10 + */ +void +gtk_cell_layout_set_cell_data_func (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + GtkCellLayoutDataFunc func, + gpointer func_data, + GDestroyNotify destroy) +{ + g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout)); + g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); + + GTK_CELL_LAYOUT_GET_IFACE + (cell_layout)->set_cell_data_func (cell_layout, cell, func, func_data, destroy); +} + +/** + * gtk_cell_layout_clear_attributes: + * @cell_layout: a `GtkCellLayout` + * @cell: a `GtkCellRenderer` to clear the attribute mapping on + * + * Clears all existing attributes previously set with + * gtk_cell_layout_set_attributes(). + * + * Deprecated: 4.10 + */ +void +gtk_cell_layout_clear_attributes (GtkCellLayout *cell_layout, + GtkCellRenderer *cell) +{ + g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout)); + g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); + + GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->clear_attributes (cell_layout, cell); +} + +/** + * gtk_cell_layout_reorder: + * @cell_layout: a `GtkCellLayout` + * @cell: a `GtkCellRenderer` to reorder + * @position: new position to insert @cell at + * + * Re-inserts @cell at @position. + * + * Note that @cell has already to be packed into @cell_layout + * for this to function properly. + * + * Deprecated: 4.10 + */ +void +gtk_cell_layout_reorder (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + int position) +{ + g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout)); + g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); + + GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->reorder (cell_layout, cell, position); +} + +/** + * gtk_cell_layout_get_cells: + * @cell_layout: a `GtkCellLayout` + * + * Returns the cell renderers which have been added to @cell_layout. + * + * Returns: (element-type GtkCellRenderer) (transfer container): + * a list of cell renderers. The list, but not the renderers has + * been newly allocated and should be freed with g_list_free() + * when no longer needed. + * + * Deprecated: 4.10 + */ +GList * +gtk_cell_layout_get_cells (GtkCellLayout *cell_layout) +{ + g_return_val_if_fail (GTK_IS_CELL_LAYOUT (cell_layout), NULL); + + return GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->get_cells (cell_layout); +} + +/** + * gtk_cell_layout_get_area: + * @cell_layout: a `GtkCellLayout` + * + * Returns the underlying `GtkCellArea` which might be @cell_layout + * if called on a `GtkCellArea` or might be %NULL if no `GtkCellArea` + * is used by @cell_layout. + * + * Returns: (transfer none) (nullable): the cell area used by @cell_layout + * + * Deprecated: 4.10 + */ +GtkCellArea * +gtk_cell_layout_get_area (GtkCellLayout *cell_layout) +{ + GtkCellLayoutIface *iface; + + g_return_val_if_fail (GTK_IS_CELL_LAYOUT (cell_layout), NULL); + + iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout); + if (iface->get_area) + return iface->get_area (cell_layout); + + return NULL; +} + +/* Attribute parsing */ +typedef struct { + GtkCellLayout *cell_layout; + GtkCellRenderer *renderer; + GtkBuilder *builder; + char *attr_name; + GString *string; +} AttributesSubParserData; + +static void +attributes_start_element (GtkBuildableParseContext *context, + const char *element_name, + const char **names, + const char **values, + gpointer user_data, + GError **error) +{ + AttributesSubParserData *data = (AttributesSubParserData*)user_data; + + if (strcmp (element_name, "attribute") == 0) + { + const char *name; + + if (!_gtk_builder_check_parent (data->builder, context, "attributes", error)) + return; + + if (!g_markup_collect_attributes (element_name, names, values, error, + G_MARKUP_COLLECT_STRING, "name", &name, + G_MARKUP_COLLECT_INVALID)) + { + _gtk_builder_prefix_error (data->builder, context, error); + return; + } + + data->attr_name = g_strdup (name); + } + else if (strcmp (element_name, "attributes") == 0) + { + if (!_gtk_builder_check_parent (data->builder, context, "child", error)) + return; + + if (!g_markup_collect_attributes (element_name, names, values, error, + G_MARKUP_COLLECT_INVALID, NULL, NULL, + G_MARKUP_COLLECT_INVALID)) + _gtk_builder_prefix_error (data->builder, context, error); + } + else + { + _gtk_builder_error_unhandled_tag (data->builder, context, + "GtkCellLayout", element_name, + error); + } +} + +static void +attributes_text_element (GtkBuildableParseContext *context, + const char *text, + gsize text_len, + gpointer user_data, + GError **error) +{ + AttributesSubParserData *data = (AttributesSubParserData*)user_data; + + if (data->attr_name) + g_string_append_len (data->string, text, text_len); +} + +static void +attributes_end_element (GtkBuildableParseContext *context, + const char *element_name, + gpointer user_data, + GError **error) +{ + AttributesSubParserData *data = (AttributesSubParserData*)user_data; + GValue val = G_VALUE_INIT; + + if (!data->attr_name) + return; + + if (!gtk_builder_value_from_string_type (data->builder, G_TYPE_INT, data->string->str, &val, error)) + { + _gtk_builder_prefix_error (data->builder, context, error); + return; + } + + gtk_cell_layout_add_attribute (data->cell_layout, + data->renderer, + data->attr_name, + g_value_get_int (&val)); + + g_free (data->attr_name); + data->attr_name = NULL; + + g_string_set_size (data->string, 0); +} + +static const GtkBuildableParser attributes_parser = + { + attributes_start_element, + attributes_end_element, + attributes_text_element + }; + + +/* Cell packing parsing */ +static void +gtk_cell_layout_buildable_set_cell_property (GtkCellArea *area, + GtkBuilder *builder, + GtkCellRenderer *cell, + char *name, + const char *value) +{ + GParamSpec *pspec; + GValue gvalue = G_VALUE_INIT; + GError *error = NULL; + + pspec = gtk_cell_area_class_find_cell_property (GTK_CELL_AREA_GET_CLASS (area), name); + if (!pspec) + { + g_warning ("%s does not have a property called %s", + g_type_name (G_OBJECT_TYPE (area)), name); + return; + } + + if (!gtk_builder_value_from_string (builder, pspec, value, &gvalue, &error)) + { + g_warning ("Could not read property %s:%s with value %s of type %s: %s", + g_type_name (G_OBJECT_TYPE (area)), + name, + value, + g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)), + error->message); + g_error_free (error); + return; + } + + gtk_cell_area_cell_set_property (area, cell, name, &gvalue); + g_value_unset (&gvalue); +} + +typedef struct { + GtkBuilder *builder; + GtkCellLayout *cell_layout; + GtkCellRenderer *renderer; + GString *string; + char *cell_prop_name; + char *context; + gboolean translatable; +} CellPackingSubParserData; + +static void +cell_packing_start_element (GtkBuildableParseContext *context, + const char *element_name, + const char **names, + const char **values, + gpointer user_data, + GError **error) +{ + CellPackingSubParserData *data = (CellPackingSubParserData*)user_data; + + if (strcmp (element_name, "property") == 0) + { + const char *name; + gboolean translatable = FALSE; + const char *ctx = NULL; + + if (!_gtk_builder_check_parent (data->builder, context, "cell-packing", error)) + return; + + if (!g_markup_collect_attributes (element_name, names, values, error, + G_MARKUP_COLLECT_STRING, "name", &name, + G_MARKUP_COLLECT_BOOLEAN|G_MARKUP_COLLECT_OPTIONAL, "translatable", &translatable, + G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "comments", NULL, + G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "context", &ctx, + G_MARKUP_COLLECT_INVALID)) + { + _gtk_builder_prefix_error (data->builder, context, error); + return; + } + + data->cell_prop_name = g_strdup (name); + data->translatable = translatable; + data->context = g_strdup (ctx); + } + else if (strcmp (element_name, "cell-packing") == 0) + { + if (!_gtk_builder_check_parent (data->builder, context, "child", error)) + return; + + if (!g_markup_collect_attributes (element_name, names, values, error, + G_MARKUP_COLLECT_INVALID, NULL, NULL, + G_MARKUP_COLLECT_INVALID)) + _gtk_builder_prefix_error (data->builder, context, error); + } + else + { + _gtk_builder_error_unhandled_tag (data->builder, context, + "GtkCellLayout", element_name, + error); + } +} + +static void +cell_packing_text_element (GtkBuildableParseContext *context, + const char *text, + gsize text_len, + gpointer user_data, + GError **error) +{ + CellPackingSubParserData *data = (CellPackingSubParserData*)user_data; + + if (data->cell_prop_name) + g_string_append_len (data->string, text, text_len); +} + +static void +cell_packing_end_element (GtkBuildableParseContext *context, + const char *element_name, + gpointer user_data, + GError **error) +{ + CellPackingSubParserData *data = (CellPackingSubParserData*)user_data; + GtkCellArea *area; + + area = gtk_cell_layout_get_area (data->cell_layout); + + if (area) + { + /* translate the string */ + if (data->string->len && data->translatable) + { + const char *translated; + const char * domain; + + domain = gtk_builder_get_translation_domain (data->builder); + + translated = _gtk_builder_parser_translate (domain, + data->context, + data->string->str); + g_string_assign (data->string, translated); + } + + if (data->cell_prop_name) + gtk_cell_layout_buildable_set_cell_property (area, + data->builder, + data->renderer, + data->cell_prop_name, + data->string->str); + } + else + g_warning ("%s does not have an internal GtkCellArea class and cannot apply child cell properties", + g_type_name (G_OBJECT_TYPE (data->cell_layout))); + + g_string_set_size (data->string, 0); + g_free (data->cell_prop_name); + g_free (data->context); + data->cell_prop_name = NULL; + data->context = NULL; + data->translatable = FALSE; +} + + +static const GtkBuildableParser cell_packing_parser = + { + cell_packing_start_element, + cell_packing_end_element, + cell_packing_text_element + }; + +gboolean +_gtk_cell_layout_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const char *tagname, + GtkBuildableParser *parser, + gpointer *data) +{ + AttributesSubParserData *attr_data; + CellPackingSubParserData *packing_data; + + if (!child) + return FALSE; + + if (strcmp (tagname, "attributes") == 0) + { + attr_data = g_slice_new0 (AttributesSubParserData); + attr_data->cell_layout = GTK_CELL_LAYOUT (buildable); + attr_data->renderer = GTK_CELL_RENDERER (child); + attr_data->builder = builder; + attr_data->attr_name = NULL; + attr_data->string = g_string_new (""); + + *parser = attributes_parser; + *data = attr_data; + + return TRUE; + } + else if (strcmp (tagname, "cell-packing") == 0) + { + packing_data = g_slice_new0 (CellPackingSubParserData); + packing_data->string = g_string_new (""); + packing_data->builder = builder; + packing_data->cell_layout = GTK_CELL_LAYOUT (buildable); + packing_data->renderer = GTK_CELL_RENDERER (child); + + *parser = cell_packing_parser; + *data = packing_data; + + return TRUE; + } + + return FALSE; +} + +gboolean +_gtk_cell_layout_buildable_custom_tag_end (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const char *tagname, + gpointer *data) +{ + AttributesSubParserData *attr_data; + CellPackingSubParserData *packing_data; + + if (strcmp (tagname, "attributes") == 0) + { + attr_data = (AttributesSubParserData*)data; + g_assert (!attr_data->attr_name); + g_string_free (attr_data->string, TRUE); + g_slice_free (AttributesSubParserData, attr_data); + + return TRUE; + } + else if (strcmp (tagname, "cell-packing") == 0) + { + packing_data = (CellPackingSubParserData *)data; + g_string_free (packing_data->string, TRUE); + g_slice_free (CellPackingSubParserData, packing_data); + + return TRUE; + } + return FALSE; +} + +void +_gtk_cell_layout_buildable_add_child (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const char *type) +{ + g_return_if_fail (GTK_IS_CELL_LAYOUT (buildable)); + g_return_if_fail (GTK_IS_CELL_RENDERER (child)); + + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (buildable), GTK_CELL_RENDERER (child), FALSE); +} diff --git a/gtk/deprecated/gtkcelllayout.h b/gtk/deprecated/gtkcelllayout.h new file mode 100644 index 0000000000..c460c34258 --- /dev/null +++ b/gtk/deprecated/gtkcelllayout.h @@ -0,0 +1,170 @@ +/* gtkcelllayout.h + * Copyright (C) 2003 Kristian Rietveld + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#ifndef __GTK_CELL_LAYOUT_H__ +#define __GTK_CELL_LAYOUT_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include +#include +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_CELL_LAYOUT (gtk_cell_layout_get_type ()) +#define GTK_CELL_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_LAYOUT, GtkCellLayout)) +#define GTK_IS_CELL_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_LAYOUT)) +#define GTK_CELL_LAYOUT_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GTK_TYPE_CELL_LAYOUT, GtkCellLayoutIface)) + +typedef struct _GtkCellLayout GtkCellLayout; /* dummy typedef */ +typedef struct _GtkCellLayoutIface GtkCellLayoutIface; + +/* keep in sync with GtkTreeCellDataFunc */ +/** + * GtkCellLayoutDataFunc: + * @cell_layout: a `GtkCellLayout` + * @cell: the cell renderer whose value is to be set + * @tree_model: the model + * @iter: a `GtkTreeIter` indicating the row to set the value for + * @data: (closure): user data passed to gtk_cell_layout_set_cell_data_func() + * + * A function which should set the value of @cell_layout’s cell renderer(s) + * as appropriate. + */ +typedef void (* GtkCellLayoutDataFunc) (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + GtkTreeModel *tree_model, + GtkTreeIter *iter, + gpointer data); + +/** + * GtkCellLayoutIface: + * @pack_start: Packs the cell into the beginning of cell_layout. + * @pack_end: Adds the cell to the end of cell_layout. + * @clear: Unsets all the mappings on all renderers on cell_layout and + * removes all renderers from cell_layout. + * @add_attribute: Adds an attribute mapping to the list in + * cell_layout. + * @set_cell_data_func: Sets the `GtkCellLayout`DataFunc to use for + * cell_layout. + * @clear_attributes: Clears all existing attributes previously set + * with gtk_cell_layout_set_attributes(). + * @reorder: Re-inserts cell at position. + * @get_cells: Get the cell renderers which have been added to + * cell_layout. + * @get_area: Get the underlying `GtkCellArea` which might be + * cell_layout if called on a `GtkCellArea` or might be NULL if no + * `GtkCellArea` is used by cell_layout. + */ +struct _GtkCellLayoutIface +{ + /*< private >*/ + GTypeInterface g_iface; + + /*< public >*/ + + /* Virtual Table */ + void (* pack_start) (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + gboolean expand); + void (* pack_end) (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + gboolean expand); + void (* clear) (GtkCellLayout *cell_layout); + void (* add_attribute) (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + const char *attribute, + int column); + void (* set_cell_data_func) (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + GtkCellLayoutDataFunc func, + gpointer func_data, + GDestroyNotify destroy); + void (* clear_attributes) (GtkCellLayout *cell_layout, + GtkCellRenderer *cell); + void (* reorder) (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + int position); + GList* (* get_cells) (GtkCellLayout *cell_layout); + + GtkCellArea *(* get_area) (GtkCellLayout *cell_layout); +}; + +GDK_AVAILABLE_IN_ALL +GType gtk_cell_layout_get_type (void) G_GNUC_CONST; +GDK_DEPRECATED_IN_4_10 +void gtk_cell_layout_pack_start (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + gboolean expand); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_layout_pack_end (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + gboolean expand); +GDK_DEPRECATED_IN_4_10 +GList *gtk_cell_layout_get_cells (GtkCellLayout *cell_layout); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_layout_clear (GtkCellLayout *cell_layout); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_layout_set_attributes (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + ...) G_GNUC_NULL_TERMINATED; +GDK_DEPRECATED_IN_4_10 +void gtk_cell_layout_add_attribute (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + const char *attribute, + int column); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_layout_set_cell_data_func (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + GtkCellLayoutDataFunc func, + gpointer func_data, + GDestroyNotify destroy); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_layout_clear_attributes (GtkCellLayout *cell_layout, + GtkCellRenderer *cell); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_layout_reorder (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + int position); +GDK_DEPRECATED_IN_4_10 +GtkCellArea *gtk_cell_layout_get_area (GtkCellLayout *cell_layout); + +gboolean _gtk_cell_layout_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const char *tagname, + GtkBuildableParser *parser, + gpointer *data); +gboolean _gtk_cell_layout_buildable_custom_tag_end (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const char *tagname, + gpointer *data); +void _gtk_cell_layout_buildable_add_child (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const char *type); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkCellLayout, g_object_unref) + +G_END_DECLS + +#endif /* __GTK_CELL_LAYOUT_H__ */ diff --git a/gtk/deprecated/gtkcellrenderer.c b/gtk/deprecated/gtkcellrenderer.c new file mode 100644 index 0000000000..a0254f8a64 --- /dev/null +++ b/gtk/deprecated/gtkcellrenderer.c @@ -0,0 +1,1802 @@ +/* gtkcellrenderer.c + * Copyright (C) 2000 Red Hat, Inc. Jonathan Blandford + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#include "config.h" + +#include "gtkcellrenderer.h" + +#include "gtkmarshalers.h" +#include "gtkprivate.h" +#include "gtksnapshot.h" +#include "gtkstylecontext.h" +#include "gtktreeprivate.h" +#include "gtktypebuiltins.h" + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + +/** + * GtkCellRenderer: + * + * An object for rendering a single cell + * + * The `GtkCellRenderer` is a base class of a set of objects used for + * rendering a cell to a `cairo_t`. These objects are used primarily by + * the `GtkTreeView` widget, though they aren’t tied to them in any + * specific way. It is worth noting that `GtkCellRenderer` is not a + * `GtkWidget` and cannot be treated as such. + * + * The primary use of a `GtkCellRenderer` is for drawing a certain graphical + * elements on a `cairo_t`. Typically, one cell renderer is used to + * draw many cells on the screen. To this extent, it isn’t expected that a + * CellRenderer keep any permanent state around. Instead, any state is set + * just prior to use using `GObject`s property system. Then, the + * cell is measured using gtk_cell_renderer_get_preferred_size(). Finally, the cell + * is rendered in the correct location using gtk_cell_renderer_snapshot(). + * + * There are a number of rules that must be followed when writing a new + * `GtkCellRenderer`. First and foremost, it’s important that a certain set + * of properties will always yield a cell renderer of the same size, + * barring a style change. The `GtkCellRenderer` also has a number of + * generic properties that are expected to be honored by all children. + * + * Beyond merely rendering a cell, cell renderers can optionally + * provide active user interface elements. A cell renderer can be + * “activatable” like `GtkCellRenderer`Toggle, + * which toggles when it gets activated by a mouse click, or it can be + * “editable” like `GtkCellRenderer`Text, which + * allows the user to edit the text using a widget implementing the + * `GtkCellEditable` interface, e.g. `GtkEntry`. + * To make a cell renderer activatable or editable, you have to + * implement the `GtkCellRenderer`Class.activate or + * `GtkCellRenderer`Class.start_editing virtual functions, respectively. + * + * Many properties of `GtkCellRenderer` and its subclasses have a + * corresponding “set” property, e.g. “cell-background-set” corresponds + * to “cell-background”. These “set” properties reflect whether a property + * has been set or not. You should not set them independently. + */ + + +#define DEBUG_CELL_SIZE_REQUEST 0 + +static void gtk_cell_renderer_init (GtkCellRenderer *cell); +static void gtk_cell_renderer_class_init (GtkCellRendererClass *class); +static void gtk_cell_renderer_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void gtk_cell_renderer_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); +static void set_cell_bg_color (GtkCellRenderer *cell, + GdkRGBA *rgba); + +static GtkSizeRequestMode gtk_cell_renderer_real_get_request_mode(GtkCellRenderer *cell); +static void gtk_cell_renderer_real_get_preferred_width (GtkCellRenderer *cell, + GtkWidget *widget, + int *minimum_size, + int *natural_size); +static void gtk_cell_renderer_real_get_preferred_height (GtkCellRenderer *cell, + GtkWidget *widget, + int *minimum_size, + int *natural_size); +static void gtk_cell_renderer_real_get_preferred_height_for_width(GtkCellRenderer *cell, + GtkWidget *widget, + int width, + int *minimum_height, + int *natural_height); +static void gtk_cell_renderer_real_get_preferred_width_for_height(GtkCellRenderer *cell, + GtkWidget *widget, + int height, + int *minimum_width, + int *natural_width); +static void gtk_cell_renderer_real_get_aligned_area (GtkCellRenderer *cell, + GtkWidget *widget, + GtkCellRendererState flags, + const GdkRectangle *cell_area, + GdkRectangle *aligned_area); + + +struct _GtkCellRendererPrivate +{ + float xalign; + float yalign; + + int width; + int height; + + guint16 xpad; + guint16 ypad; + + guint mode : 2; + guint visible : 1; + guint is_expander : 1; + guint is_expanded : 1; + guint cell_background_set : 1; + guint sensitive : 1; + guint editing : 1; + + GdkRGBA cell_background; +}; + +enum { + PROP_0, + PROP_MODE, + PROP_VISIBLE, + PROP_SENSITIVE, + PROP_XALIGN, + PROP_YALIGN, + PROP_XPAD, + PROP_YPAD, + PROP_WIDTH, + PROP_HEIGHT, + PROP_IS_EXPANDER, + PROP_IS_EXPANDED, + PROP_CELL_BACKGROUND, + PROP_CELL_BACKGROUND_RGBA, + PROP_CELL_BACKGROUND_SET, + PROP_EDITING +}; + +/* Signal IDs */ +enum { + EDITING_CANCELED, + EDITING_STARTED, + LAST_SIGNAL +}; + +static int GtkCellRenderer_private_offset; +static guint cell_renderer_signals[LAST_SIGNAL] = { 0 }; + +static inline gpointer +gtk_cell_renderer_get_instance_private (GtkCellRenderer *self) +{ + return (G_STRUCT_MEMBER_P (self, GtkCellRenderer_private_offset)); +} + +static void +gtk_cell_renderer_init (GtkCellRenderer *cell) +{ + GtkCellRendererPrivate *priv; + + cell->priv = gtk_cell_renderer_get_instance_private (cell); + priv = cell->priv; + + priv->mode = GTK_CELL_RENDERER_MODE_INERT; + priv->visible = TRUE; + priv->width = -1; + priv->height = -1; + priv->xalign = 0.5; + priv->yalign = 0.5; + priv->xpad = 0; + priv->ypad = 0; + priv->sensitive = TRUE; + priv->is_expander = FALSE; + priv->is_expanded = FALSE; + priv->editing = FALSE; +} + +static void +gtk_cell_renderer_class_init (GtkCellRendererClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + object_class->get_property = gtk_cell_renderer_get_property; + object_class->set_property = gtk_cell_renderer_set_property; + + class->snapshot = NULL; + class->get_request_mode = gtk_cell_renderer_real_get_request_mode; + class->get_preferred_width = gtk_cell_renderer_real_get_preferred_width; + class->get_preferred_height = gtk_cell_renderer_real_get_preferred_height; + class->get_preferred_width_for_height = gtk_cell_renderer_real_get_preferred_width_for_height; + class->get_preferred_height_for_width = gtk_cell_renderer_real_get_preferred_height_for_width; + class->get_aligned_area = gtk_cell_renderer_real_get_aligned_area; + + /** + * GtkCellRenderer::editing-canceled: + * @renderer: the object which received the signal + * + * This signal gets emitted when the user cancels the process of editing a + * cell. For example, an editable cell renderer could be written to cancel + * editing when the user presses Escape. + * + * See also: gtk_cell_renderer_stop_editing(). + */ + cell_renderer_signals[EDITING_CANCELED] = + g_signal_new (I_("editing-canceled"), + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GtkCellRendererClass, editing_canceled), + NULL, NULL, + NULL, + G_TYPE_NONE, 0); + + /** + * GtkCellRenderer::editing-started: + * @renderer: the object which received the signal + * @editable: the `GtkCellEditable` + * @path: the path identifying the edited cell + * + * This signal gets emitted when a cell starts to be edited. + * The intended use of this signal is to do special setup + * on @editable, e.g. adding a `GtkEntryCompletion` or setting + * up additional columns in a `GtkComboBox`. + * + * See gtk_cell_editable_start_editing() for information on the lifecycle of + * the @editable and a way to do setup that doesn’t depend on the @renderer. + * + * Note that GTK doesn't guarantee that cell renderers will + * continue to use the same kind of widget for editing in future + * releases, therefore you should check the type of @editable + * before doing any specific setup, as in the following example: + * |[ + * static void + * text_editing_started (GtkCellRenderer *cell, + * GtkCellEditable *editable, + * const char *path, + * gpointer data) + * { + * if (GTK_IS_ENTRY (editable)) + * { + * GtkEntry *entry = GTK_ENTRY (editable); + * + * // ... create a GtkEntryCompletion + * + * gtk_entry_set_completion (entry, completion); + * } + * } + * ]| + */ + cell_renderer_signals[EDITING_STARTED] = + g_signal_new (I_("editing-started"), + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GtkCellRendererClass, editing_started), + NULL, NULL, + _gtk_marshal_VOID__OBJECT_STRING, + G_TYPE_NONE, 2, + GTK_TYPE_CELL_EDITABLE, + G_TYPE_STRING); + g_signal_set_va_marshaller (cell_renderer_signals[EDITING_STARTED], + G_TYPE_FROM_CLASS (object_class), + _gtk_marshal_VOID__OBJECT_STRINGv); + + g_object_class_install_property (object_class, + PROP_MODE, + g_param_spec_enum ("mode", NULL, NULL, + GTK_TYPE_CELL_RENDERER_MODE, + GTK_CELL_RENDERER_MODE_INERT, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + g_object_class_install_property (object_class, + PROP_VISIBLE, + g_param_spec_boolean ("visible", NULL, NULL, + TRUE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + g_object_class_install_property (object_class, + PROP_SENSITIVE, + g_param_spec_boolean ("sensitive", NULL, NULL, + TRUE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + g_object_class_install_property (object_class, + PROP_XALIGN, + g_param_spec_float ("xalign", NULL, NULL, + 0.0, + 1.0, + 0.5, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + g_object_class_install_property (object_class, + PROP_YALIGN, + g_param_spec_float ("yalign", NULL, NULL, + 0.0, + 1.0, + 0.5, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + g_object_class_install_property (object_class, + PROP_XPAD, + g_param_spec_uint ("xpad", NULL, NULL, + 0, + G_MAXUINT, + 0, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + g_object_class_install_property (object_class, + PROP_YPAD, + g_param_spec_uint ("ypad", NULL, NULL, + 0, + G_MAXUINT, + 0, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + g_object_class_install_property (object_class, + PROP_WIDTH, + g_param_spec_int ("width", NULL, NULL, + -1, + G_MAXINT, + -1, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + g_object_class_install_property (object_class, + PROP_HEIGHT, + g_param_spec_int ("height", NULL, NULL, + -1, + G_MAXINT, + -1, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + g_object_class_install_property (object_class, + PROP_IS_EXPANDER, + g_param_spec_boolean ("is-expander", NULL, NULL, + FALSE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + + g_object_class_install_property (object_class, + PROP_IS_EXPANDED, + g_param_spec_boolean ("is-expanded", NULL, NULL, + FALSE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + g_object_class_install_property (object_class, + PROP_CELL_BACKGROUND, + g_param_spec_string ("cell-background", NULL, NULL, + NULL, + GTK_PARAM_WRITABLE)); + + /** + * GtkCellRenderer:cell-background-rgba: + * + * Cell background as a `GdkRGBA` + */ + g_object_class_install_property (object_class, + PROP_CELL_BACKGROUND_RGBA, + g_param_spec_boxed ("cell-background-rgba", NULL, NULL, + GDK_TYPE_RGBA, + GTK_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_EDITING, + g_param_spec_boolean ("editing", NULL, NULL, + FALSE, + GTK_PARAM_READABLE)); + + +#define ADD_SET_PROP(propname, propval, nick, blurb) g_object_class_install_property (object_class, propval, g_param_spec_boolean (propname, nick, blurb, FALSE, GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)) + + ADD_SET_PROP ("cell-background-set", PROP_CELL_BACKGROUND_SET, NULL, NULL); + + if (GtkCellRenderer_private_offset != 0) + g_type_class_adjust_private_offset (class, &GtkCellRenderer_private_offset); +} + +GType +gtk_cell_renderer_get_type (void) +{ + static GType cell_renderer_type = 0; + + if (G_UNLIKELY (cell_renderer_type == 0)) + { + const GTypeInfo cell_renderer_info = + { + sizeof (GtkCellRendererClass), + NULL, + NULL, + (GClassInitFunc) gtk_cell_renderer_class_init, + NULL, /* class_finalize */ + NULL, /* class_init */ + sizeof (GtkWidget), + 0, /* n_preallocs */ + (GInstanceInitFunc) gtk_cell_renderer_init, + NULL, /* value_table */ + }; + cell_renderer_type = g_type_register_static (G_TYPE_INITIALLY_UNOWNED, "GtkCellRenderer", + &cell_renderer_info, G_TYPE_FLAG_ABSTRACT); + + GtkCellRenderer_private_offset = + g_type_add_instance_private (cell_renderer_type, sizeof (GtkCellRendererPrivate)); + } + + return cell_renderer_type; +} + +static void +gtk_cell_renderer_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + GtkCellRenderer *cell = GTK_CELL_RENDERER (object); + GtkCellRendererPrivate *priv = cell->priv; + + switch (param_id) + { + case PROP_MODE: + g_value_set_enum (value, priv->mode); + break; + case PROP_VISIBLE: + g_value_set_boolean (value, priv->visible); + break; + case PROP_SENSITIVE: + g_value_set_boolean (value, priv->sensitive); + break; + case PROP_EDITING: + g_value_set_boolean (value, priv->editing); + break; + case PROP_XALIGN: + g_value_set_float (value, priv->xalign); + break; + case PROP_YALIGN: + g_value_set_float (value, priv->yalign); + break; + case PROP_XPAD: + g_value_set_uint (value, priv->xpad); + break; + case PROP_YPAD: + g_value_set_uint (value, priv->ypad); + break; + case PROP_WIDTH: + g_value_set_int (value, priv->width); + break; + case PROP_HEIGHT: + g_value_set_int (value, priv->height); + break; + case PROP_IS_EXPANDER: + g_value_set_boolean (value, priv->is_expander); + break; + case PROP_IS_EXPANDED: + g_value_set_boolean (value, priv->is_expanded); + break; + case PROP_CELL_BACKGROUND_RGBA: + g_value_set_boxed (value, &priv->cell_background); + break; + case PROP_CELL_BACKGROUND_SET: + g_value_set_boolean (value, priv->cell_background_set); + break; + case PROP_CELL_BACKGROUND: + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } + +} + +static void +gtk_cell_renderer_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkCellRenderer *cell = GTK_CELL_RENDERER (object); + GtkCellRendererPrivate *priv = cell->priv; + + switch (param_id) + { + case PROP_MODE: + if (priv->mode != g_value_get_enum (value)) + { + priv->mode = g_value_get_enum (value); + g_object_notify_by_pspec (object, pspec); + } + break; + case PROP_VISIBLE: + if (priv->visible != g_value_get_boolean (value)) + { + priv->visible = g_value_get_boolean (value); + g_object_notify_by_pspec (object, pspec); + } + break; + case PROP_SENSITIVE: + if (priv->sensitive != g_value_get_boolean (value)) + { + priv->sensitive = g_value_get_boolean (value); + g_object_notify_by_pspec (object, pspec); + } + break; + case PROP_XALIGN: + if (priv->xalign != g_value_get_float (value)) + { + priv->xalign = g_value_get_float (value); + g_object_notify_by_pspec (object, pspec); + } + break; + case PROP_YALIGN: + if (priv->yalign != g_value_get_float (value)) + { + priv->yalign = g_value_get_float (value); + g_object_notify_by_pspec (object, pspec); + } + break; + case PROP_XPAD: + if (priv->xpad != g_value_get_uint (value)) + { + priv->xpad = g_value_get_uint (value); + g_object_notify_by_pspec (object, pspec); + } + break; + case PROP_YPAD: + if (priv->ypad != g_value_get_uint (value)) + { + priv->ypad = g_value_get_uint (value); + g_object_notify_by_pspec (object, pspec); + } + break; + case PROP_WIDTH: + if (priv->width != g_value_get_int (value)) + { + priv->width = g_value_get_int (value); + g_object_notify_by_pspec (object, pspec); + } + break; + case PROP_HEIGHT: + if (priv->height != g_value_get_int (value)) + { + priv->height = g_value_get_int (value); + g_object_notify_by_pspec (object, pspec); + } + break; + case PROP_IS_EXPANDER: + if (priv->is_expander != g_value_get_boolean (value)) + { + priv->is_expander = g_value_get_boolean (value); + g_object_notify_by_pspec (object, pspec); + } + break; + case PROP_IS_EXPANDED: + if (priv->is_expanded != g_value_get_boolean (value)) + { + priv->is_expanded = g_value_get_boolean (value); + g_object_notify_by_pspec (object, pspec); + } + break; + case PROP_CELL_BACKGROUND: + { + GdkRGBA rgba; + + if (!g_value_get_string (value)) + set_cell_bg_color (cell, NULL); + else if (gdk_rgba_parse (&rgba, g_value_get_string (value))) + set_cell_bg_color (cell, &rgba); + else + g_warning ("Don't know color '%s'", g_value_get_string (value)); + + g_object_notify (object, "cell-background"); + } + break; + case PROP_CELL_BACKGROUND_RGBA: + set_cell_bg_color (cell, g_value_get_boxed (value)); + break; + case PROP_CELL_BACKGROUND_SET: + if (priv->cell_background_set != g_value_get_boolean (value)) + { + priv->cell_background_set = g_value_get_boolean (value); + g_object_notify (object, "cell-background-set"); + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +set_cell_bg_color (GtkCellRenderer *cell, + GdkRGBA *rgba) +{ + GtkCellRendererPrivate *priv = cell->priv; + + if (rgba) + { + if (!priv->cell_background_set) + { + priv->cell_background_set = TRUE; + g_object_notify (G_OBJECT (cell), "cell-background-set"); + } + + priv->cell_background = *rgba; + } + else + { + if (priv->cell_background_set) + { + priv->cell_background_set = FALSE; + g_object_notify (G_OBJECT (cell), "cell-background-set"); + } + } + g_object_notify (G_OBJECT (cell), "cell-background-rgba"); +} + +/** + * gtk_cell_renderer_snapshot: + * @cell: a `GtkCellRenderer` + * @snapshot: a `GtkSnapshot` to draw to + * @widget: the widget owning @window + * @background_area: entire cell area (including tree expanders and maybe + * padding on the sides) + * @cell_area: area normally rendered by a cell renderer + * @flags: flags that affect rendering + * + * Invokes the virtual render function of the `GtkCellRenderer`. The three + * passed-in rectangles are areas in @cr. Most renderers will draw within + * @cell_area; the xalign, yalign, xpad, and ypad fields of the `GtkCellRenderer` + * should be honored with respect to @cell_area. @background_area includes the + * blank space around the cell, and also the area containing the tree expander; + * so the @background_area rectangles for all cells tile to cover the entire + * @window. + * + * Deprecated: 4.10 + **/ +void +gtk_cell_renderer_snapshot (GtkCellRenderer *cell, + GtkSnapshot *snapshot, + GtkWidget *widget, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + GtkCellRendererState flags) +{ + gboolean selected = FALSE; + GtkCellRendererPrivate *priv = cell->priv; + GtkStyleContext *context; + GtkStateFlags state; + + g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); + g_return_if_fail (GTK_CELL_RENDERER_GET_CLASS (cell)->snapshot != NULL); + g_return_if_fail (snapshot != NULL); + + selected = (flags & GTK_CELL_RENDERER_SELECTED) == GTK_CELL_RENDERER_SELECTED; + + gtk_snapshot_push_debug (snapshot, "%s", G_OBJECT_TYPE_NAME (cell)); + + if (priv->cell_background_set && !selected) + { + gtk_snapshot_append_color (snapshot, + &priv->cell_background, + &GRAPHENE_RECT_INIT ( + background_area->x, background_area->y, + background_area->width, background_area->height + )); + } + + gtk_snapshot_push_clip (snapshot, + &GRAPHENE_RECT_INIT ( + background_area->x, background_area->y, + background_area->width, background_area->height + )); + + context = gtk_widget_get_style_context (widget); + + gtk_style_context_save (context); + gtk_style_context_add_class (context, "cell"); + + state = gtk_cell_renderer_get_state (cell, widget, flags); + gtk_style_context_set_state (context, state); + + GTK_CELL_RENDERER_GET_CLASS (cell)->snapshot (cell, + snapshot, + widget, + background_area, + cell_area, + flags); + gtk_style_context_restore (context); + gtk_snapshot_pop (snapshot); + gtk_snapshot_pop (snapshot); +} + +/** + * gtk_cell_renderer_activate: + * @cell: a `GtkCellRenderer` + * @event: a `GdkEvent` + * @widget: widget that received the event + * @path: widget-dependent string representation of the event location; + * e.g. for `GtkTreeView`, a string representation of `GtkTreePath` + * @background_area: background area as passed to gtk_cell_renderer_render() + * @cell_area: cell area as passed to gtk_cell_renderer_render() + * @flags: render flags + * + * Passes an activate event to the cell renderer for possible processing. + * Some cell renderers may use events; for example, `GtkCellRendererToggle` + * toggles when it gets a mouse click. + * + * Returns: %TRUE if the event was consumed/handled + * + * Deprecated: 4.10 + **/ +gboolean +gtk_cell_renderer_activate (GtkCellRenderer *cell, + GdkEvent *event, + GtkWidget *widget, + const char *path, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + GtkCellRendererState flags) +{ + GtkCellRendererPrivate *priv; + + g_return_val_if_fail (GTK_IS_CELL_RENDERER (cell), FALSE); + + priv = cell->priv; + + if (priv->mode != GTK_CELL_RENDERER_MODE_ACTIVATABLE) + return FALSE; + + if (GTK_CELL_RENDERER_GET_CLASS (cell)->activate == NULL) + return FALSE; + + return GTK_CELL_RENDERER_GET_CLASS (cell)->activate (cell, + event, + widget, + path, + (GdkRectangle *) background_area, + (GdkRectangle *) cell_area, + flags); +} + +/** + * gtk_cell_renderer_start_editing: + * @cell: a `GtkCellRenderer` + * @event: (nullable): a `GdkEvent` + * @widget: widget that received the event + * @path: widget-dependent string representation of the event location; + * e.g. for `GtkTreeView`, a string representation of `GtkTreePath` + * @background_area: background area as passed to gtk_cell_renderer_render() + * @cell_area: cell area as passed to gtk_cell_renderer_render() + * @flags: render flags + * + * Starts editing the contents of this @cell, through a new `GtkCellEditable` + * widget created by the `GtkCellRenderer`Class.start_editing virtual function. + * + * Returns: (nullable) (transfer none): A new `GtkCellEditable` for editing this + * @cell, or %NULL if editing is not possible + * + * Deprecated: 4.10 + **/ +GtkCellEditable * +gtk_cell_renderer_start_editing (GtkCellRenderer *cell, + GdkEvent *event, + GtkWidget *widget, + const char *path, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + GtkCellRendererState flags) + +{ + GtkCellRendererPrivate *priv; + GtkCellEditable *editable; + + g_return_val_if_fail (GTK_IS_CELL_RENDERER (cell), NULL); + + priv = cell->priv; + + if (priv->mode != GTK_CELL_RENDERER_MODE_EDITABLE) + return NULL; + + if (GTK_CELL_RENDERER_GET_CLASS (cell)->start_editing == NULL) + return NULL; + + editable = GTK_CELL_RENDERER_GET_CLASS (cell)->start_editing (cell, + event, + widget, + path, + (GdkRectangle *) background_area, + (GdkRectangle *) cell_area, + flags); + if (editable == NULL) + return NULL; + + gtk_widget_add_css_class (GTK_WIDGET (editable), "cell"); + + g_signal_emit (cell, + cell_renderer_signals[EDITING_STARTED], 0, + editable, path); + + priv->editing = TRUE; + + return editable; +} + +/** + * gtk_cell_renderer_set_fixed_size: + * @cell: A `GtkCellRenderer` + * @width: the width of the cell renderer, or -1 + * @height: the height of the cell renderer, or -1 + * + * Sets the renderer size to be explicit, independent of the properties set. + * + * Deprecated: 4.10 + **/ +void +gtk_cell_renderer_set_fixed_size (GtkCellRenderer *cell, + int width, + int height) +{ + GtkCellRendererPrivate *priv; + + g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); + g_return_if_fail (width >= -1 && height >= -1); + + priv = cell->priv; + + if ((width != priv->width) || (height != priv->height)) + { + g_object_freeze_notify (G_OBJECT (cell)); + + if (width != priv->width) + { + priv->width = width; + g_object_notify (G_OBJECT (cell), "width"); + } + + if (height != priv->height) + { + priv->height = height; + g_object_notify (G_OBJECT (cell), "height"); + } + + g_object_thaw_notify (G_OBJECT (cell)); + } +} + +/** + * gtk_cell_renderer_get_fixed_size: + * @cell: A `GtkCellRenderer` + * @width: (out) (optional): location to fill in with the fixed width of the cell + * @height: (out) (optional): location to fill in with the fixed height of the cell + * + * Fills in @width and @height with the appropriate size of @cell. + * + * Deprecated: 4.10 + */ +void +gtk_cell_renderer_get_fixed_size (GtkCellRenderer *cell, + int *width, + int *height) +{ + GtkCellRendererPrivate *priv; + + g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); + + priv = cell->priv; + + if (width) + *width = priv->width; + if (height) + *height = priv->height; +} + +/** + * gtk_cell_renderer_set_alignment: + * @cell: A `GtkCellRenderer` + * @xalign: the x alignment of the cell renderer + * @yalign: the y alignment of the cell renderer + * + * Sets the renderer’s alignment within its available space. + * + * Deprecated: 4.10 + **/ +void +gtk_cell_renderer_set_alignment (GtkCellRenderer *cell, + float xalign, + float yalign) +{ + GtkCellRendererPrivate *priv; + + g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); + g_return_if_fail (xalign >= 0.0 && xalign <= 1.0); + g_return_if_fail (yalign >= 0.0 && yalign <= 1.0); + + priv = cell->priv; + + if ((xalign != priv->xalign) || (yalign != priv->yalign)) + { + g_object_freeze_notify (G_OBJECT (cell)); + + if (xalign != priv->xalign) + { + priv->xalign = xalign; + g_object_notify (G_OBJECT (cell), "xalign"); + } + + if (yalign != priv->yalign) + { + priv->yalign = yalign; + g_object_notify (G_OBJECT (cell), "yalign"); + } + + g_object_thaw_notify (G_OBJECT (cell)); + } +} + +/** + * gtk_cell_renderer_get_alignment: + * @cell: A `GtkCellRenderer` + * @xalign: (out) (optional): location to fill in with the x alignment of the cell + * @yalign: (out) (optional): location to fill in with the y alignment of the cell + * + * Fills in @xalign and @yalign with the appropriate values of @cell. + * + * Deprecated: 4.10 + */ +void +gtk_cell_renderer_get_alignment (GtkCellRenderer *cell, + float *xalign, + float *yalign) +{ + GtkCellRendererPrivate *priv; + + g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); + + priv = cell->priv; + + if (xalign) + *xalign = priv->xalign; + if (yalign) + *yalign = priv->yalign; +} + +/** + * gtk_cell_renderer_set_padding: + * @cell: A `GtkCellRenderer` + * @xpad: the x padding of the cell renderer + * @ypad: the y padding of the cell renderer + * + * Sets the renderer’s padding. + * + * Deprecated: 4.10 + **/ +void +gtk_cell_renderer_set_padding (GtkCellRenderer *cell, + int xpad, + int ypad) +{ + GtkCellRendererPrivate *priv; + + g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); + g_return_if_fail (xpad >= 0 && ypad >= 0); + + priv = cell->priv; + + if ((xpad != priv->xpad) || (ypad != priv->ypad)) + { + g_object_freeze_notify (G_OBJECT (cell)); + + if (xpad != priv->xpad) + { + priv->xpad = xpad; + g_object_notify (G_OBJECT (cell), "xpad"); + } + + if (ypad != priv->ypad) + { + priv->ypad = ypad; + g_object_notify (G_OBJECT (cell), "ypad"); + } + + g_object_thaw_notify (G_OBJECT (cell)); + } +} + +/** + * gtk_cell_renderer_get_padding: + * @cell: A `GtkCellRenderer` + * @xpad: (out) (optional): location to fill in with the x padding of the cell + * @ypad: (out) (optional): location to fill in with the y padding of the cell + * + * Fills in @xpad and @ypad with the appropriate values of @cell. + * + * Deprecated: 4.10 + */ +void +gtk_cell_renderer_get_padding (GtkCellRenderer *cell, + int *xpad, + int *ypad) +{ + GtkCellRendererPrivate *priv; + + g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); + + priv = cell->priv; + + if (xpad) + *xpad = priv->xpad; + if (ypad) + *ypad = priv->ypad; +} + +/** + * gtk_cell_renderer_set_visible: + * @cell: A `GtkCellRenderer` + * @visible: the visibility of the cell + * + * Sets the cell renderer’s visibility. + * + * Deprecated: 4.10 + **/ +void +gtk_cell_renderer_set_visible (GtkCellRenderer *cell, + gboolean visible) +{ + GtkCellRendererPrivate *priv; + + g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); + + priv = cell->priv; + + if (priv->visible != visible) + { + priv->visible = visible ? TRUE : FALSE; + g_object_notify (G_OBJECT (cell), "visible"); + } +} + +/** + * gtk_cell_renderer_get_visible: + * @cell: A `GtkCellRenderer` + * + * Returns the cell renderer’s visibility. + * + * Returns: %TRUE if the cell renderer is visible + * + * Deprecated: 4.10 + */ +gboolean +gtk_cell_renderer_get_visible (GtkCellRenderer *cell) +{ + g_return_val_if_fail (GTK_IS_CELL_RENDERER (cell), FALSE); + + return cell->priv->visible; +} + +/** + * gtk_cell_renderer_set_sensitive: + * @cell: A `GtkCellRenderer` + * @sensitive: the sensitivity of the cell + * + * Sets the cell renderer’s sensitivity. + * + * Deprecated: 4.10 + **/ +void +gtk_cell_renderer_set_sensitive (GtkCellRenderer *cell, + gboolean sensitive) +{ + GtkCellRendererPrivate *priv; + + g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); + + priv = cell->priv; + + if (priv->sensitive != sensitive) + { + priv->sensitive = sensitive ? TRUE : FALSE; + g_object_notify (G_OBJECT (cell), "sensitive"); + } +} + +/** + * gtk_cell_renderer_get_sensitive: + * @cell: A `GtkCellRenderer` + * + * Returns the cell renderer’s sensitivity. + * + * Returns: %TRUE if the cell renderer is sensitive + * + * Deprecated: 4.10 + */ +gboolean +gtk_cell_renderer_get_sensitive (GtkCellRenderer *cell) +{ + g_return_val_if_fail (GTK_IS_CELL_RENDERER (cell), FALSE); + + return cell->priv->sensitive; +} + + +/** + * gtk_cell_renderer_is_activatable: + * @cell: A `GtkCellRenderer` + * + * Checks whether the cell renderer can do something when activated. + * + * Returns: %TRUE if the cell renderer can do anything when activated + * + * Deprecated: 4.10 + */ +gboolean +gtk_cell_renderer_is_activatable (GtkCellRenderer *cell) +{ + GtkCellRendererPrivate *priv; + + g_return_val_if_fail (GTK_IS_CELL_RENDERER (cell), FALSE); + + priv = cell->priv; + + return (priv->visible && + (priv->mode == GTK_CELL_RENDERER_MODE_EDITABLE || + priv->mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE)); +} + + +/** + * gtk_cell_renderer_stop_editing: + * @cell: A `GtkCellRenderer` + * @canceled: %TRUE if the editing has been canceled + * + * Informs the cell renderer that the editing is stopped. + * If @canceled is %TRUE, the cell renderer will emit the + * `GtkCellRenderer`::editing-canceled signal. + * + * This function should be called by cell renderer implementations + * in response to the `GtkCellEditable::editing-done` signal of + * `GtkCellEditable`. + * + * Deprecated: 4.10 + **/ +void +gtk_cell_renderer_stop_editing (GtkCellRenderer *cell, + gboolean canceled) +{ + GtkCellRendererPrivate *priv; + + g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); + + priv = cell->priv; + + if (priv->editing) + { + priv->editing = FALSE; + if (canceled) + g_signal_emit (cell, cell_renderer_signals[EDITING_CANCELED], 0); + } +} + +static void +gtk_cell_renderer_real_get_preferred_size (GtkCellRenderer *cell, + GtkWidget *widget, + GtkOrientation orientation, + int *minimum_size, + int *natural_size) +{ + GtkRequisition min_req; + + min_req.width = 0; + min_req.height = 0; + + if (orientation == GTK_ORIENTATION_HORIZONTAL) + { + if (minimum_size) + *minimum_size = min_req.width; + + if (natural_size) + *natural_size = min_req.width; + } + else + { + if (minimum_size) + *minimum_size = min_req.height; + + if (natural_size) + *natural_size = min_req.height; + } +} + +static GtkSizeRequestMode +gtk_cell_renderer_real_get_request_mode (GtkCellRenderer *cell) +{ + /* By default cell renderers are height-for-width. */ + return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH; +} + +static void +gtk_cell_renderer_real_get_preferred_width (GtkCellRenderer *cell, + GtkWidget *widget, + int *minimum_size, + int *natural_size) +{ + gtk_cell_renderer_real_get_preferred_size (cell, widget, GTK_ORIENTATION_HORIZONTAL, + minimum_size, natural_size); +} + +static void +gtk_cell_renderer_real_get_preferred_height (GtkCellRenderer *cell, + GtkWidget *widget, + int *minimum_size, + int *natural_size) +{ + gtk_cell_renderer_real_get_preferred_size (cell, widget, GTK_ORIENTATION_VERTICAL, + minimum_size, natural_size); +} + + +static void +gtk_cell_renderer_real_get_preferred_height_for_width (GtkCellRenderer *cell, + GtkWidget *widget, + int width, + int *minimum_height, + int *natural_height) +{ + gtk_cell_renderer_get_preferred_height (cell, widget, minimum_height, natural_height); +} + +static void +gtk_cell_renderer_real_get_preferred_width_for_height (GtkCellRenderer *cell, + GtkWidget *widget, + int height, + int *minimum_width, + int *natural_width) +{ + gtk_cell_renderer_get_preferred_width (cell, widget, minimum_width, natural_width); +} + + +/* Default implementation assumes that a cell renderer will never use more + * space than its natural size (this is fine for toggles and pixbufs etc + * but needs to be overridden from wrapping/ellipsizing text renderers) */ +static void +gtk_cell_renderer_real_get_aligned_area (GtkCellRenderer *cell, + GtkWidget *widget, + GtkCellRendererState flags, + const GdkRectangle *cell_area, + GdkRectangle *aligned_area) +{ + int opposite_size, x_offset, y_offset; + int natural_size; + + g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); + g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (cell_area != NULL); + g_return_if_fail (aligned_area != NULL); + + *aligned_area = *cell_area; + + /* Trim up the aligned size */ + if (gtk_cell_renderer_get_request_mode (cell) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH) + { + gtk_cell_renderer_get_preferred_width (cell, widget, + NULL, &natural_size); + + aligned_area->width = MIN (aligned_area->width, natural_size); + + gtk_cell_renderer_get_preferred_height_for_width (cell, widget, + aligned_area->width, + NULL, &opposite_size); + + aligned_area->height = MIN (opposite_size, aligned_area->height); + } + else + { + gtk_cell_renderer_get_preferred_height (cell, widget, + NULL, &natural_size); + + aligned_area->height = MIN (aligned_area->width, natural_size); + + gtk_cell_renderer_get_preferred_width_for_height (cell, widget, + aligned_area->height, + NULL, &opposite_size); + + aligned_area->width = MIN (opposite_size, aligned_area->width); + } + + /* offset the cell position */ + _gtk_cell_renderer_calc_offset (cell, cell_area, + gtk_widget_get_direction (widget), + aligned_area->width, + aligned_area->height, + &x_offset, &y_offset); + + aligned_area->x += x_offset; + aligned_area->y += y_offset; +} + + +/* An internal convenience function for some containers to peek at the + * cell alignment in a target allocation (used to draw focus and align + * cells in the icon view). + * + * Note this is only a trivial “align * (allocation - request)” operation. + */ +void +_gtk_cell_renderer_calc_offset (GtkCellRenderer *cell, + const GdkRectangle *cell_area, + GtkTextDirection direction, + int width, + int height, + int *x_offset, + int *y_offset) +{ + GtkCellRendererPrivate *priv; + + g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); + g_return_if_fail (cell_area != NULL); + g_return_if_fail (x_offset || y_offset); + + priv = cell->priv; + + if (x_offset) + { + *x_offset = (((direction == GTK_TEXT_DIR_RTL) ? + (1.0 - priv->xalign) : priv->xalign) * + (cell_area->width - width)); + *x_offset = MAX (*x_offset, 0); + } + if (y_offset) + { + *y_offset = (priv->yalign * + (cell_area->height - height)); + *y_offset = MAX (*y_offset, 0); + } +} + +/** + * gtk_cell_renderer_get_request_mode: + * @cell: a `GtkCellRenderer` instance + * + * Gets whether the cell renderer prefers a height-for-width layout + * or a width-for-height layout. + * + * Returns: The `GtkSizeRequestMode` preferred by this renderer. + * + * Deprecated: 4.10 + */ +GtkSizeRequestMode +gtk_cell_renderer_get_request_mode (GtkCellRenderer *cell) +{ + g_return_val_if_fail (GTK_IS_CELL_RENDERER (cell), FALSE); + + return GTK_CELL_RENDERER_GET_CLASS (cell)->get_request_mode (cell); +} + +/** + * gtk_cell_renderer_get_preferred_width: + * @cell: a `GtkCellRenderer` instance + * @widget: the `GtkWidget` this cell will be rendering to + * @minimum_size: (out) (optional): location to store the minimum size + * @natural_size: (out) (optional): location to store the natural size + * + * Retrieves a renderer’s natural size when rendered to @widget. + * + * Deprecated: 4.10 + */ +void +gtk_cell_renderer_get_preferred_width (GtkCellRenderer *cell, + GtkWidget *widget, + int *minimum_size, + int *natural_size) +{ + GtkCellRendererClass *klass; + int width; + + g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); + g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (NULL != minimum_size || NULL != natural_size); + + gtk_cell_renderer_get_fixed_size (GTK_CELL_RENDERER (cell), &width, NULL); + + if (width < 0) + { + klass = GTK_CELL_RENDERER_GET_CLASS (cell); + klass->get_preferred_width (cell, widget, minimum_size, natural_size); + } + else + { + if (minimum_size) + *minimum_size = width; + if (natural_size) + *natural_size = width; + } + +#if DEBUG_CELL_SIZE_REQUEST + g_message ("%s returning minimum width: %d and natural width: %d", + G_OBJECT_TYPE_NAME (cell), + minimum_size ? *minimum_size : 20000, + natural_size ? *natural_size : 20000); +#endif +} + + +/** + * gtk_cell_renderer_get_preferred_height: + * @cell: a `GtkCellRenderer` instance + * @widget: the `GtkWidget` this cell will be rendering to + * @minimum_size: (out) (optional): location to store the minimum size + * @natural_size: (out) (optional): location to store the natural size + * + * Retrieves a renderer’s natural size when rendered to @widget. + * + * Deprecated: 4.10 + */ +void +gtk_cell_renderer_get_preferred_height (GtkCellRenderer *cell, + GtkWidget *widget, + int *minimum_size, + int *natural_size) +{ + GtkCellRendererClass *klass; + int height; + + g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); + g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (NULL != minimum_size || NULL != natural_size); + + gtk_cell_renderer_get_fixed_size (GTK_CELL_RENDERER (cell), NULL, &height); + + if (height < 0) + { + klass = GTK_CELL_RENDERER_GET_CLASS (cell); + klass->get_preferred_height (cell, widget, minimum_size, natural_size); + } + else + { + if (minimum_size) + *minimum_size = height; + if (natural_size) + *natural_size = height; + } + +#if DEBUG_CELL_SIZE_REQUEST + g_message ("%s returning minimum height: %d and natural height: %d", + G_OBJECT_TYPE_NAME (cell), + minimum_size ? *minimum_size : 20000, + natural_size ? *natural_size : 20000); +#endif +} + + +/** + * gtk_cell_renderer_get_preferred_width_for_height: + * @cell: a `GtkCellRenderer` instance + * @widget: the `GtkWidget` this cell will be rendering to + * @height: the size which is available for allocation + * @minimum_width: (out) (optional): location for storing the minimum size + * @natural_width: (out) (optional): location for storing the preferred size + * + * Retrieves a cell renderers’s minimum and natural width if it were rendered to + * @widget with the specified @height. + * + * Deprecated: 4.10 + */ +void +gtk_cell_renderer_get_preferred_width_for_height (GtkCellRenderer *cell, + GtkWidget *widget, + int height, + int *minimum_width, + int *natural_width) +{ + GtkCellRendererClass *klass; + int width; + + g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); + g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (NULL != minimum_width || NULL != natural_width); + + gtk_cell_renderer_get_fixed_size (GTK_CELL_RENDERER (cell), &width, NULL); + + if (width < 0) + { + klass = GTK_CELL_RENDERER_GET_CLASS (cell); + klass->get_preferred_width_for_height (cell, widget, height, minimum_width, natural_width); + } + else + { + if (minimum_width) + *minimum_width = width; + if (natural_width) + *natural_width = width; + } + +#if DEBUG_CELL_SIZE_REQUEST + g_message ("%s width for height: %d is minimum %d and natural: %d", + G_OBJECT_TYPE_NAME (cell), height, + minimum_width ? *minimum_width : 20000, + natural_width ? *natural_width : 20000); +#endif +} + +/** + * gtk_cell_renderer_get_preferred_height_for_width: + * @cell: a `GtkCellRenderer` instance + * @widget: the `GtkWidget` this cell will be rendering to + * @width: the size which is available for allocation + * @minimum_height: (out) (optional): location for storing the minimum size + * @natural_height: (out) (optional): location for storing the preferred size + * + * Retrieves a cell renderers’s minimum and natural height if it were rendered to + * @widget with the specified @width. + * + * Deprecated: 4.10 + */ +void +gtk_cell_renderer_get_preferred_height_for_width (GtkCellRenderer *cell, + GtkWidget *widget, + int width, + int *minimum_height, + int *natural_height) +{ + GtkCellRendererClass *klass; + int height; + + g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); + g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (NULL != minimum_height || NULL != natural_height); + + gtk_cell_renderer_get_fixed_size (GTK_CELL_RENDERER (cell), NULL, &height); + + if (height < 0) + { + klass = GTK_CELL_RENDERER_GET_CLASS (cell); + klass->get_preferred_height_for_width (cell, widget, width, minimum_height, natural_height); + } + else + { + if (minimum_height) + *minimum_height = height; + if (natural_height) + *natural_height = height; + } + +#if DEBUG_CELL_SIZE_REQUEST + g_message ("%s height for width: %d is minimum %d and natural: %d", + G_OBJECT_TYPE_NAME (cell), width, + minimum_height ? *minimum_height : 20000, + natural_height ? *natural_height : 20000); +#endif +} + +/** + * gtk_cell_renderer_get_preferred_size: + * @cell: a `GtkCellRenderer` instance + * @widget: the `GtkWidget` this cell will be rendering to + * @minimum_size: (out) (optional): location for storing the minimum size + * @natural_size: (out) (optional): location for storing the natural size + * + * Retrieves the minimum and natural size of a cell taking + * into account the widget’s preference for height-for-width management. + * + * Deprecated: 4.10 + */ +void +gtk_cell_renderer_get_preferred_size (GtkCellRenderer *cell, + GtkWidget *widget, + GtkRequisition *minimum_size, + GtkRequisition *natural_size) +{ + int min_width, nat_width; + int min_height, nat_height; + + g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); + + if (gtk_cell_renderer_get_request_mode (cell) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH) + { + gtk_cell_renderer_get_preferred_width (cell, widget, &min_width, &nat_width); + + if (minimum_size) + { + minimum_size->width = min_width; + gtk_cell_renderer_get_preferred_height_for_width (cell, widget, min_width, + &minimum_size->height, NULL); + } + + if (natural_size) + { + natural_size->width = nat_width; + gtk_cell_renderer_get_preferred_height_for_width (cell, widget, nat_width, + NULL, &natural_size->height); + } + } + else /* GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT */ + { + gtk_cell_renderer_get_preferred_height (cell, widget, &min_height, &nat_height); + + if (minimum_size) + { + minimum_size->height = min_height; + gtk_cell_renderer_get_preferred_width_for_height (cell, widget, min_height, + &minimum_size->width, NULL); + } + + if (natural_size) + { + natural_size->height = nat_height; + gtk_cell_renderer_get_preferred_width_for_height (cell, widget, nat_height, + NULL, &natural_size->width); + } + } +} + +/** + * gtk_cell_renderer_get_aligned_area: + * @cell: a `GtkCellRenderer` instance + * @widget: the `GtkWidget` this cell will be rendering to + * @flags: render flags + * @cell_area: cell area which would be passed to gtk_cell_renderer_render() + * @aligned_area: (out): the return location for the space inside @cell_area + * that would actually be used to render. + * + * Gets the aligned area used by @cell inside @cell_area. Used for finding + * the appropriate edit and focus rectangle. + * + * Deprecated: 4.10 + */ +void +gtk_cell_renderer_get_aligned_area (GtkCellRenderer *cell, + GtkWidget *widget, + GtkCellRendererState flags, + const GdkRectangle *cell_area, + GdkRectangle *aligned_area) +{ + GtkCellRendererClass *klass; + + g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); + g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (cell_area != NULL); + g_return_if_fail (aligned_area != NULL); + + klass = GTK_CELL_RENDERER_GET_CLASS (cell); + klass->get_aligned_area (cell, widget, flags, cell_area, aligned_area); + + g_assert (aligned_area->x >= cell_area->x && aligned_area->x <= cell_area->x + cell_area->width); + g_assert (aligned_area->y >= cell_area->y && aligned_area->y <= cell_area->y + cell_area->height); + g_assert ((aligned_area->x - cell_area->x) + aligned_area->width <= cell_area->width); + g_assert ((aligned_area->y - cell_area->y) + aligned_area->height <= cell_area->height); +} + +/** + * gtk_cell_renderer_get_state: + * @cell: (nullable): a `GtkCellRenderer` + * @widget: (nullable): a `GtkWidget` + * @cell_state: cell renderer state + * + * Translates the cell renderer state to `GtkStateFlags`, + * based on the cell renderer and widget sensitivity, and + * the given `GtkCellRenderer`State. + * + * Returns: the widget state flags applying to @cell + * + * Deprecated: 4.10 + **/ +GtkStateFlags +gtk_cell_renderer_get_state (GtkCellRenderer *cell, + GtkWidget *widget, + GtkCellRendererState cell_state) +{ + GtkStateFlags state = 0; + + g_return_val_if_fail (!cell || GTK_IS_CELL_RENDERER (cell), 0); + g_return_val_if_fail (!widget || GTK_IS_WIDGET (widget), 0); + + if (widget) + state |= gtk_widget_get_state_flags (widget); + + state &= ~(GTK_STATE_FLAG_FOCUSED | GTK_STATE_FLAG_PRELIGHT | GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_DROP_ACTIVE); + + if ((state & GTK_STATE_FLAG_INSENSITIVE) != 0 || + (cell && !gtk_cell_renderer_get_sensitive (cell)) || + (cell_state & GTK_CELL_RENDERER_INSENSITIVE) != 0) + { + state |= GTK_STATE_FLAG_INSENSITIVE; + } + else + { + if ((widget && gtk_widget_has_focus (widget)) && + (cell_state & GTK_CELL_RENDERER_FOCUSED) != 0) + state |= GTK_STATE_FLAG_FOCUSED; + + if ((cell_state & GTK_CELL_RENDERER_PRELIT) != 0) + state |= GTK_STATE_FLAG_PRELIGHT; + } + + if ((cell_state & GTK_CELL_RENDERER_SELECTED) != 0) + state |= GTK_STATE_FLAG_SELECTED; + + return state; +} + +/** + * gtk_cell_renderer_set_is_expander: + * @cell: a `GtkCellRenderer` + * @is_expander: whether @cell is an expander + * + * Sets whether the given `GtkCellRenderer` is an expander. + * + * Deprecated: 4.10 + */ +void +gtk_cell_renderer_set_is_expander (GtkCellRenderer *cell, + gboolean is_expander) +{ + GtkCellRendererPrivate *priv = gtk_cell_renderer_get_instance_private (cell); + + g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); + + is_expander = !!is_expander; + + if (is_expander != priv->is_expander) + { + priv->is_expander = is_expander; + + g_object_notify (G_OBJECT (cell), "is-expander"); + } +} + +/** + * gtk_cell_renderer_get_is_expander: + * @cell: a `GtkCellRenderer` + * + * Checks whether the given `GtkCellRenderer` is an expander. + * + * Returns: %TRUE if @cell is an expander, and %FALSE otherwise + * + * Deprecated: 4.10 + */ +gboolean +gtk_cell_renderer_get_is_expander (GtkCellRenderer *cell) +{ + GtkCellRendererPrivate *priv = gtk_cell_renderer_get_instance_private (cell); + + g_return_val_if_fail (GTK_IS_CELL_RENDERER (cell), FALSE); + + return priv->is_expander; +} + +/** + * gtk_cell_renderer_set_is_expanded: + * @cell: a `GtkCellRenderer` + * @is_expanded: whether @cell should be expanded + * + * Sets whether the given `GtkCellRenderer` is expanded. + * + * Deprecated: 4.10 + */ +void +gtk_cell_renderer_set_is_expanded (GtkCellRenderer *cell, + gboolean is_expanded) +{ + GtkCellRendererPrivate *priv = gtk_cell_renderer_get_instance_private (cell); + + g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); + + is_expanded = !!is_expanded; + + if (is_expanded != priv->is_expanded) + { + priv->is_expanded = is_expanded; + + g_object_notify (G_OBJECT (cell), "is-expanded"); + } +} + +/** + * gtk_cell_renderer_get_is_expanded: + * @cell: a `GtkCellRenderer` + * + * Checks whether the given `GtkCellRenderer` is expanded. + * + * Returns: %TRUE if the cell renderer is expanded + * + * Deprecated: 4.10 + */ +gboolean +gtk_cell_renderer_get_is_expanded (GtkCellRenderer *cell) +{ + GtkCellRendererPrivate *priv = gtk_cell_renderer_get_instance_private (cell); + + g_return_val_if_fail (GTK_IS_CELL_RENDERER (cell), FALSE); + + return priv->is_expanded; +} diff --git a/gtk/deprecated/gtkcellrenderer.h b/gtk/deprecated/gtkcellrenderer.h new file mode 100644 index 0000000000..dabcc7aac8 --- /dev/null +++ b/gtk/deprecated/gtkcellrenderer.h @@ -0,0 +1,313 @@ +/* gtkcellrenderer.h + * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#ifndef __GTK_CELL_RENDERER_H__ +#define __GTK_CELL_RENDERER_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + + +/** + * GtkCellRendererState: + * @GTK_CELL_RENDERER_SELECTED: The cell is currently selected, and + * probably has a selection colored background to render to. + * @GTK_CELL_RENDERER_PRELIT: The mouse is hovering over the cell. + * @GTK_CELL_RENDERER_INSENSITIVE: The cell is drawn in an insensitive manner + * @GTK_CELL_RENDERER_SORTED: The cell is in a sorted row + * @GTK_CELL_RENDERER_FOCUSED: The cell is in the focus row. + * @GTK_CELL_RENDERER_EXPANDABLE: The cell is in a row that can be expanded + * @GTK_CELL_RENDERER_EXPANDED: The cell is in a row that is expanded + * + * Tells how a cell is to be rendered. + */ +typedef enum +{ + GTK_CELL_RENDERER_SELECTED = 1 << 0, + GTK_CELL_RENDERER_PRELIT = 1 << 1, + GTK_CELL_RENDERER_INSENSITIVE = 1 << 2, + /* this flag means the cell is in the sort column/row */ + GTK_CELL_RENDERER_SORTED = 1 << 3, + GTK_CELL_RENDERER_FOCUSED = 1 << 4, + GTK_CELL_RENDERER_EXPANDABLE = 1 << 5, + GTK_CELL_RENDERER_EXPANDED = 1 << 6 +} GtkCellRendererState; + +/** + * GtkCellRendererMode: + * @GTK_CELL_RENDERER_MODE_INERT: The cell is just for display + * and cannot be interacted with. Note that this doesn’t mean that eg. the + * row being drawn can’t be selected -- just that a particular element of + * it cannot be individually modified. + * @GTK_CELL_RENDERER_MODE_ACTIVATABLE: The cell can be clicked. + * @GTK_CELL_RENDERER_MODE_EDITABLE: The cell can be edited or otherwise modified. + * + * Identifies how the user can interact with a particular cell. + */ +typedef enum +{ + GTK_CELL_RENDERER_MODE_INERT, + GTK_CELL_RENDERER_MODE_ACTIVATABLE, + GTK_CELL_RENDERER_MODE_EDITABLE +} GtkCellRendererMode; + +#define GTK_TYPE_CELL_RENDERER (gtk_cell_renderer_get_type ()) +#define GTK_CELL_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_RENDERER, GtkCellRenderer)) +#define GTK_CELL_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_CELL_RENDERER, GtkCellRendererClass)) +#define GTK_IS_CELL_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_RENDERER)) +#define GTK_IS_CELL_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_CELL_RENDERER)) +#define GTK_CELL_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CELL_RENDERER, GtkCellRendererClass)) + +typedef struct _GtkCellRenderer GtkCellRenderer; +typedef struct _GtkCellRendererPrivate GtkCellRendererPrivate; +typedef struct _GtkCellRendererClass GtkCellRendererClass; +typedef struct _GtkCellRendererClassPrivate GtkCellRendererClassPrivate; + +struct _GtkCellRenderer +{ + GInitiallyUnowned parent_instance; + + /*< private >*/ + GtkCellRendererPrivate *priv; +}; + +/** + * GtkCellRendererClass: + * @get_request_mode: Called to gets whether the cell renderer prefers + * a height-for-width layout or a width-for-height layout. + * @get_preferred_width: Called to get a renderer’s natural width. + * @get_preferred_height_for_width: Called to get a renderer’s natural height for width. + * @get_preferred_height: Called to get a renderer’s natural height. + * @get_preferred_width_for_height: Called to get a renderer’s natural width for height. + * @get_aligned_area: Called to get the aligned area used by @cell inside @cell_area. + * @snapshot: Called to snapshot the content of the `GtkCellRenderer`. + * @activate: Called to activate the content of the `GtkCellRenderer`. + * @start_editing: Called to initiate editing the content of the `GtkCellRenderer`. + * @editing_canceled: Signal gets emitted when the user cancels the process of editing a cell. + * @editing_started: Signal gets emitted when a cell starts to be edited. + */ +struct _GtkCellRendererClass +{ + /*< private >*/ + GInitiallyUnownedClass parent_class; + + /*< public >*/ + + /* vtable - not signals */ + GtkSizeRequestMode (* get_request_mode) (GtkCellRenderer *cell); + void (* get_preferred_width) (GtkCellRenderer *cell, + GtkWidget *widget, + int *minimum_size, + int *natural_size); + void (* get_preferred_height_for_width) (GtkCellRenderer *cell, + GtkWidget *widget, + int width, + int *minimum_height, + int *natural_height); + void (* get_preferred_height) (GtkCellRenderer *cell, + GtkWidget *widget, + int *minimum_size, + int *natural_size); + void (* get_preferred_width_for_height) (GtkCellRenderer *cell, + GtkWidget *widget, + int height, + int *minimum_width, + int *natural_width); + void (* get_aligned_area) (GtkCellRenderer *cell, + GtkWidget *widget, + GtkCellRendererState flags, + const GdkRectangle *cell_area, + GdkRectangle *aligned_area); + void (* snapshot) (GtkCellRenderer *cell, + GtkSnapshot *snapshot, + GtkWidget *widget, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + GtkCellRendererState flags); + gboolean (* activate) (GtkCellRenderer *cell, + GdkEvent *event, + GtkWidget *widget, + const char *path, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + GtkCellRendererState flags); + GtkCellEditable * (* start_editing) (GtkCellRenderer *cell, + GdkEvent *event, + GtkWidget *widget, + const char *path, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + GtkCellRendererState flags); + + /* Signals */ + void (* editing_canceled) (GtkCellRenderer *cell); + void (* editing_started) (GtkCellRenderer *cell, + GtkCellEditable *editable, + const char *path); + + /*< private >*/ + gpointer padding[8]; +}; + +GDK_AVAILABLE_IN_ALL +GType gtk_cell_renderer_get_type (void) G_GNUC_CONST; + +GDK_DEPRECATED_IN_4_10 +GtkSizeRequestMode gtk_cell_renderer_get_request_mode (GtkCellRenderer *cell); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_renderer_get_preferred_width (GtkCellRenderer *cell, + GtkWidget *widget, + int *minimum_size, + int *natural_size); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_renderer_get_preferred_height_for_width (GtkCellRenderer *cell, + GtkWidget *widget, + int width, + int *minimum_height, + int *natural_height); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_renderer_get_preferred_height (GtkCellRenderer *cell, + GtkWidget *widget, + int *minimum_size, + int *natural_size); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_renderer_get_preferred_width_for_height (GtkCellRenderer *cell, + GtkWidget *widget, + int height, + int *minimum_width, + int *natural_width); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_renderer_get_preferred_size (GtkCellRenderer *cell, + GtkWidget *widget, + GtkRequisition *minimum_size, + GtkRequisition *natural_size); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_renderer_get_aligned_area (GtkCellRenderer *cell, + GtkWidget *widget, + GtkCellRendererState flags, + const GdkRectangle *cell_area, + GdkRectangle *aligned_area); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_renderer_snapshot (GtkCellRenderer *cell, + GtkSnapshot *snapshot, + GtkWidget *widget, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + GtkCellRendererState flags); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_cell_renderer_activate (GtkCellRenderer *cell, + GdkEvent *event, + GtkWidget *widget, + const char *path, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + GtkCellRendererState flags); +GDK_DEPRECATED_IN_4_10 +GtkCellEditable *gtk_cell_renderer_start_editing (GtkCellRenderer *cell, + GdkEvent *event, + GtkWidget *widget, + const char *path, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + GtkCellRendererState flags); + +GDK_DEPRECATED_IN_4_10 +void gtk_cell_renderer_set_fixed_size (GtkCellRenderer *cell, + int width, + int height); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_renderer_get_fixed_size (GtkCellRenderer *cell, + int *width, + int *height); + +GDK_DEPRECATED_IN_4_10 +void gtk_cell_renderer_set_alignment (GtkCellRenderer *cell, + float xalign, + float yalign); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_renderer_get_alignment (GtkCellRenderer *cell, + float *xalign, + float *yalign); + +GDK_DEPRECATED_IN_4_10 +void gtk_cell_renderer_set_padding (GtkCellRenderer *cell, + int xpad, + int ypad); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_renderer_get_padding (GtkCellRenderer *cell, + int *xpad, + int *ypad); + +GDK_DEPRECATED_IN_4_10 +void gtk_cell_renderer_set_visible (GtkCellRenderer *cell, + gboolean visible); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_cell_renderer_get_visible (GtkCellRenderer *cell); + +GDK_DEPRECATED_IN_4_10 +void gtk_cell_renderer_set_sensitive (GtkCellRenderer *cell, + gboolean sensitive); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_cell_renderer_get_sensitive (GtkCellRenderer *cell); + +GDK_DEPRECATED_IN_4_10 +gboolean gtk_cell_renderer_is_activatable (GtkCellRenderer *cell); + +GDK_DEPRECATED_IN_4_10 +void gtk_cell_renderer_set_is_expander (GtkCellRenderer *cell, + gboolean is_expander); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_cell_renderer_get_is_expander (GtkCellRenderer *cell); + +GDK_DEPRECATED_IN_4_10 +void gtk_cell_renderer_set_is_expanded (GtkCellRenderer *cell, + gboolean is_expanded); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_cell_renderer_get_is_expanded (GtkCellRenderer *cell); + + + + +/* For use by cell renderer implementations only */ +GDK_DEPRECATED_IN_4_10 +void gtk_cell_renderer_stop_editing (GtkCellRenderer *cell, + gboolean canceled); + + +void _gtk_cell_renderer_calc_offset (GtkCellRenderer *cell, + const GdkRectangle *cell_area, + GtkTextDirection direction, + int width, + int height, + int *x_offset, + int *y_offset); + +GDK_DEPRECATED_IN_4_10 +GtkStateFlags gtk_cell_renderer_get_state (GtkCellRenderer *cell, + GtkWidget *widget, + GtkCellRendererState cell_state); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkCellRenderer, g_object_unref) + +G_END_DECLS + +#endif /* __GTK_CELL_RENDERER_H__ */ diff --git a/gtk/deprecated/gtkcellrendereraccel.c b/gtk/deprecated/gtkcellrendereraccel.c new file mode 100644 index 0000000000..34c5e329c9 --- /dev/null +++ b/gtk/deprecated/gtkcellrendereraccel.c @@ -0,0 +1,732 @@ +/* gtkcellrendereraccel.h + * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#include "config.h" + +#include "gtkcellrendereraccel.h" + +#include +#include "gtkaccelgroup.h" +#include "gtkmarshalers.h" +#include "gtklabel.h" +#include "gtkmain.h" +#include "gtksizerequest.h" +#include "gtktypebuiltins.h" +#include "gtkprivate.h" +#include "gtkeventcontrollerkey.h" +#include "gtknative.h" +#include "gtkbinlayout.h" + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + +/** + * GtkCellRendererAccel: + * + * Renders a keyboard accelerator in a cell + * + * `GtkCellRendererAccel` displays a keyboard accelerator (i.e. a key + * combination like `Control + a`). If the cell renderer is editable, + * the accelerator can be changed by simply typing the new combination. + */ + + +static void gtk_cell_renderer_accel_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void gtk_cell_renderer_accel_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); +static void gtk_cell_renderer_accel_get_preferred_width + (GtkCellRenderer *cell, + GtkWidget *widget, + int *minimum_size, + int *natural_size); +static GtkCellEditable * + gtk_cell_renderer_accel_start_editing (GtkCellRenderer *cell, + GdkEvent *event, + GtkWidget *widget, + const char *path, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + GtkCellRendererState flags); +static char *convert_keysym_state_to_string (GtkCellRendererAccel *accel, + guint keysym, + GdkModifierType mask, + guint keycode); +static GtkWidget *gtk_cell_editable_widget_new (GtkCellRenderer *cell, + GtkCellRendererAccelMode mode, + const char *path); + +enum { + ACCEL_EDITED, + ACCEL_CLEARED, + LAST_SIGNAL +}; + +enum { + PROP_ACCEL_KEY = 1, + PROP_ACCEL_MODS, + PROP_KEYCODE, + PROP_ACCEL_MODE +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +typedef struct _GtkCellRendererAccelPrivate GtkCellRendererAccelPrivate; +typedef struct _GtkCellRendererAccelClass GtkCellRendererAccelClass; + +struct _GtkCellRendererAccel +{ + GtkCellRendererText parent; +}; + +struct _GtkCellRendererAccelClass +{ + GtkCellRendererTextClass parent_class; + + void (* accel_edited) (GtkCellRendererAccel *accel, + const char *path_string, + guint accel_key, + GdkModifierType accel_mods, + guint hardware_keycode); + + void (* accel_cleared) (GtkCellRendererAccel *accel, + const char *path_string); + + /* Padding for future expansion */ + void (*_gtk_reserved0) (void); + void (*_gtk_reserved1) (void); + void (*_gtk_reserved2) (void); + void (*_gtk_reserved3) (void); + void (*_gtk_reserved4) (void); +}; + +struct _GtkCellRendererAccelPrivate +{ + GtkWidget *sizing_label; + + GtkCellRendererAccelMode accel_mode; + GdkModifierType accel_mods; + guint accel_key; + guint keycode; +}; + +G_DEFINE_TYPE_WITH_PRIVATE (GtkCellRendererAccel, gtk_cell_renderer_accel, GTK_TYPE_CELL_RENDERER_TEXT) + +static void +gtk_cell_renderer_accel_init (GtkCellRendererAccel *cell_accel) +{ + char *text; + + text = convert_keysym_state_to_string (cell_accel, 0, 0, 0); + g_object_set (cell_accel, "text", text, NULL); + g_free (text); +} + +static void +gtk_cell_renderer_accel_dispose (GObject *object) +{ + GtkCellRendererAccelPrivate *priv = gtk_cell_renderer_accel_get_instance_private (GTK_CELL_RENDERER_ACCEL (object)); + + g_clear_object (&priv->sizing_label); + + G_OBJECT_CLASS (gtk_cell_renderer_accel_parent_class)->dispose (object); +} + +static void +gtk_cell_renderer_accel_class_init (GtkCellRendererAccelClass *cell_accel_class) +{ + GObjectClass *object_class; + GtkCellRendererClass *cell_renderer_class; + + object_class = G_OBJECT_CLASS (cell_accel_class); + cell_renderer_class = GTK_CELL_RENDERER_CLASS (cell_accel_class); + + object_class->set_property = gtk_cell_renderer_accel_set_property; + object_class->get_property = gtk_cell_renderer_accel_get_property; + object_class->dispose = gtk_cell_renderer_accel_dispose; + + cell_renderer_class->get_preferred_width = gtk_cell_renderer_accel_get_preferred_width; + cell_renderer_class->start_editing = gtk_cell_renderer_accel_start_editing; + + /** + * GtkCellRendererAccel:accel-key: + * + * The keyval of the accelerator. + */ + g_object_class_install_property (object_class, + PROP_ACCEL_KEY, + g_param_spec_uint ("accel-key", NULL, NULL, + 0, + G_MAXINT, + 0, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + /** + * GtkCellRendererAccel:accel-mods: + * + * The modifier mask of the accelerator. + */ + g_object_class_install_property (object_class, + PROP_ACCEL_MODS, + g_param_spec_flags ("accel-mods", NULL, NULL, + GDK_TYPE_MODIFIER_TYPE, + 0, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + /** + * GtkCellRendererAccel:keycode: + * + * The hardware keycode of the accelerator. Note that the hardware keycode is + * only relevant if the key does not have a keyval. Normally, the keyboard + * configuration should assign keyvals to all keys. + */ + g_object_class_install_property (object_class, + PROP_KEYCODE, + g_param_spec_uint ("keycode", NULL, NULL, + 0, + G_MAXINT, + 0, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + /** + * GtkCellRendererAccel:accel-mode: + * + * Determines if the edited accelerators are GTK accelerators. If + * they are, consumed modifiers are suppressed, only accelerators + * accepted by GTK are allowed, and the accelerators are rendered + * in the same way as they are in menus. + */ + g_object_class_install_property (object_class, + PROP_ACCEL_MODE, + g_param_spec_enum ("accel-mode", NULL, NULL, + GTK_TYPE_CELL_RENDERER_ACCEL_MODE, + GTK_CELL_RENDERER_ACCEL_MODE_GTK, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + /** + * GtkCellRendererAccel::accel-edited: + * @accel: the object reveiving the signal + * @path_string: the path identifying the row of the edited cell + * @accel_key: the new accelerator keyval + * @accel_mods: the new acclerator modifier mask + * @hardware_keycode: the keycode of the new accelerator + * + * Gets emitted when the user has selected a new accelerator. + */ + signals[ACCEL_EDITED] = g_signal_new (I_("accel-edited"), + GTK_TYPE_CELL_RENDERER_ACCEL, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkCellRendererAccelClass, accel_edited), + NULL, NULL, + _gtk_marshal_VOID__STRING_UINT_FLAGS_UINT, + G_TYPE_NONE, 4, + G_TYPE_STRING, + G_TYPE_UINT, + GDK_TYPE_MODIFIER_TYPE, + G_TYPE_UINT); + + /** + * GtkCellRendererAccel::accel-cleared: + * @accel: the object reveiving the signal + * @path_string: the path identifying the row of the edited cell + * + * Gets emitted when the user has removed the accelerator. + */ + signals[ACCEL_CLEARED] = g_signal_new (I_("accel-cleared"), + GTK_TYPE_CELL_RENDERER_ACCEL, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkCellRendererAccelClass, accel_cleared), + NULL, NULL, + NULL, + G_TYPE_NONE, 1, + G_TYPE_STRING); +} + + +/** + * gtk_cell_renderer_accel_new: + * + * Creates a new `GtkCellRendererAccel`. + * + * Returns: the new cell renderer + * + * Deprecated: 4.10 + */ +GtkCellRenderer * +gtk_cell_renderer_accel_new (void) +{ + return g_object_new (GTK_TYPE_CELL_RENDERER_ACCEL, NULL); +} + +static char * +convert_keysym_state_to_string (GtkCellRendererAccel *accel, + guint keysym, + GdkModifierType mask, + guint keycode) +{ + GtkCellRendererAccelPrivate *priv = gtk_cell_renderer_accel_get_instance_private (accel); + + if (keysym == 0 && keycode == 0) + /* This label is displayed in a treeview cell displaying + * a disabled accelerator key combination. + */ + return g_strdup (C_("Accelerator", "Disabled")); + else + { + if (priv->accel_mode == GTK_CELL_RENDERER_ACCEL_MODE_GTK) + { + if (!gtk_accelerator_valid (keysym, mask)) + /* This label is displayed in a treeview cell displaying + * an accelerator key combination that is not valid according + * to gtk_accelerator_valid(). + */ + return g_strdup (C_("Accelerator", "Invalid")); + + return gtk_accelerator_get_label (keysym, mask); + } + else + { + char *name; + + name = gtk_accelerator_get_label_with_keycode (NULL, keysym, keycode, mask); + if (name == NULL) + name = gtk_accelerator_name_with_keycode (NULL, keysym, keycode, mask); + + return name; + } + } +} + +static void +gtk_cell_renderer_accel_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + GtkCellRendererAccelPrivate *priv = gtk_cell_renderer_accel_get_instance_private (GTK_CELL_RENDERER_ACCEL (object)); + + switch (param_id) + { + case PROP_ACCEL_KEY: + g_value_set_uint (value, priv->accel_key); + break; + + case PROP_ACCEL_MODS: + g_value_set_flags (value, priv->accel_mods); + break; + + case PROP_KEYCODE: + g_value_set_uint (value, priv->keycode); + break; + + case PROP_ACCEL_MODE: + g_value_set_enum (value, priv->accel_mode); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + } +} + +static void +gtk_cell_renderer_accel_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkCellRendererAccel *accel = GTK_CELL_RENDERER_ACCEL (object); + GtkCellRendererAccelPrivate *priv = gtk_cell_renderer_accel_get_instance_private (GTK_CELL_RENDERER_ACCEL (object)); + gboolean changed = FALSE; + + switch (param_id) + { + case PROP_ACCEL_KEY: + { + guint accel_key = g_value_get_uint (value); + + if (priv->accel_key != accel_key) + { + priv->accel_key = accel_key; + changed = TRUE; + g_object_notify (object, "accel-key"); + } + } + break; + + case PROP_ACCEL_MODS: + { + guint accel_mods = g_value_get_flags (value); + + if (priv->accel_mods != accel_mods) + { + priv->accel_mods = accel_mods; + changed = TRUE; + g_object_notify (object, "accel-mods"); + } + } + break; + case PROP_KEYCODE: + { + guint keycode = g_value_get_uint (value); + + if (priv->keycode != keycode) + { + priv->keycode = keycode; + changed = TRUE; + g_object_notify (object, "keycode"); + } + } + break; + + case PROP_ACCEL_MODE: + if (priv->accel_mode != g_value_get_enum (value)) + { + priv->accel_mode = g_value_get_enum (value); + g_object_notify (object, "accel-mode"); + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + } + + if (changed) + { + char *text; + + text = convert_keysym_state_to_string (accel, priv->accel_key, priv->accel_mods, priv->keycode); + g_object_set (accel, "text", text, NULL); + g_free (text); + } +} + +static void +gtk_cell_renderer_accel_get_preferred_width (GtkCellRenderer *cell, + GtkWidget *widget, + int *minimum_size, + int *natural_size) + +{ + GtkCellRendererAccelPrivate *priv = gtk_cell_renderer_accel_get_instance_private (GTK_CELL_RENDERER_ACCEL (cell)); + GtkRequisition min_req, nat_req; + + if (priv->sizing_label == NULL) + { + priv->sizing_label = gtk_label_new (_("New accelerator…")); + g_object_ref_sink (priv->sizing_label); + } + + gtk_widget_get_preferred_size (priv->sizing_label, &min_req, &nat_req); + + GTK_CELL_RENDERER_CLASS (gtk_cell_renderer_accel_parent_class)->get_preferred_width (cell, widget, + minimum_size, natural_size); + + /* FIXME: need to take the cell_area et al. into account */ + if (minimum_size) + *minimum_size = MAX (*minimum_size, min_req.width); + if (natural_size) + *natural_size = MAX (*natural_size, nat_req.width); +} + +static GtkCellEditable * +gtk_cell_renderer_accel_start_editing (GtkCellRenderer *cell, + GdkEvent *event, + GtkWidget *widget, + const char *path, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + GtkCellRendererState flags) +{ + GtkCellRendererText *celltext = GTK_CELL_RENDERER_TEXT (cell); + GtkCellRendererAccel *accel = GTK_CELL_RENDERER_ACCEL (cell); + GtkCellRendererAccelPrivate *priv = gtk_cell_renderer_accel_get_instance_private (accel); + GtkWidget *editable; + gboolean is_editable; + + /* If the cell isn't editable we return NULL. */ + g_object_get (celltext, "editable", &is_editable, NULL); + if (!is_editable) + return NULL; + + editable = gtk_cell_editable_widget_new (cell, priv->accel_mode, path); + + return GTK_CELL_EDITABLE (editable); +} + +/* --------------------------------- */ + +typedef struct _GtkCellEditableWidget GtkCellEditableWidget; +typedef GtkWidgetClass GtkCellEditableWidgetClass; + +struct _GtkCellEditableWidget +{ + GtkWidget parent; + + gboolean editing_canceled; + GtkCellRendererAccelMode accel_mode; + char *path; + GtkCellRenderer *cell; + GtkWidget *label; +}; + +enum { + PROP_EDITING_CANCELED = 1, + PROP_MODE, + PROP_PATH +}; + +GType gtk_cell_editable_widget_get_type (void); +static void gtk_cell_editable_widget_cell_editable_init (GtkCellEditableIface *iface); + +G_DEFINE_TYPE_WITH_CODE (GtkCellEditableWidget, gtk_cell_editable_widget, GTK_TYPE_WIDGET, + G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_EDITABLE, gtk_cell_editable_widget_cell_editable_init)) + +static void +gtk_cell_editable_widget_start_editing (GtkCellEditable *cell_editable, + GdkEvent *event) +{ + /* do nothing, because we are pointless */ +} + +static void +gtk_cell_editable_widget_cell_editable_init (GtkCellEditableIface *iface) +{ + iface->start_editing = gtk_cell_editable_widget_start_editing; +} + +static gboolean +key_controller_modifiers (GtkEventControllerKey *key, + GdkModifierType state, + GtkWidget *widget) +{ + /* Ignore modifiers */ + return TRUE; +} + +static gboolean +key_controller_key_pressed (GtkEventControllerKey *key, + guint keyval, + guint keycode, + GdkModifierType state, + GtkWidget *widget) +{ + GtkCellEditableWidget *box = (GtkCellEditableWidget*)widget; + gboolean edited = FALSE; + gboolean cleared = FALSE; + GdkModifierType accel_mods = 0; + guint accel_key; + GdkEvent *event; + + event = gtk_event_controller_get_current_event (GTK_EVENT_CONTROLLER (key)); + if (!gdk_key_event_get_match (event, &accel_key, &accel_mods)) + return FALSE; + + if (accel_mods == 0) + { + switch (keyval) + { + case GDK_KEY_BackSpace: + cleared = TRUE; + G_GNUC_FALLTHROUGH; + case GDK_KEY_Escape: + goto out; + default: + break; + } + } + + if (box->accel_mode == GTK_CELL_RENDERER_ACCEL_MODE_GTK && + !gtk_accelerator_valid (accel_key, accel_mods)) + { + gtk_widget_error_bell (widget); + return TRUE; + } + + edited = TRUE; + + out: + gtk_grab_remove (widget); + gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (widget)); + gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (widget)); + + if (edited) + g_signal_emit (box->cell, signals[ACCEL_EDITED], 0, box->path, + accel_key, accel_mods, keycode); + else if (cleared) + g_signal_emit (box->cell, signals[ACCEL_CLEARED], 0, box->path); + + return TRUE; +} + +static void +gtk_cell_editable_widget_unrealize (GtkWidget *widget) +{ + gtk_grab_remove (widget); + + GTK_WIDGET_CLASS (gtk_cell_editable_widget_parent_class)->unrealize (widget); +} + +static void +gtk_cell_editable_widget_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkCellEditableWidget *box = (GtkCellEditableWidget*)object; + + switch (prop_id) + { + case PROP_EDITING_CANCELED: + box->editing_canceled = g_value_get_boolean (value); + break; + case PROP_MODE: + box->accel_mode = g_value_get_enum (value); + break; + case PROP_PATH: + box->path = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_cell_editable_widget_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkCellEditableWidget *box = (GtkCellEditableWidget*)object; + + switch (prop_id) + { + case PROP_EDITING_CANCELED: + g_value_set_boolean (value, box->editing_canceled); + break; + case PROP_MODE: + g_value_set_enum (value, box->accel_mode); + break; + case PROP_PATH: + g_value_set_string (value, box->path); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_cell_editable_widget_dispose (GObject *object) +{ + GtkCellEditableWidget *box = (GtkCellEditableWidget*)object; + + g_clear_pointer (&box->label, gtk_widget_unparent); + + G_OBJECT_CLASS (gtk_cell_editable_widget_parent_class)->dispose (object); +} + +static void +gtk_cell_editable_widget_finalize (GObject *object) +{ + GtkCellEditableWidget *box = (GtkCellEditableWidget*)object; + + g_free (box->path); + + G_OBJECT_CLASS (gtk_cell_editable_widget_parent_class)->finalize (object); +} + +static void +gtk_cell_editable_widget_class_init (GtkCellEditableWidgetClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); + + object_class->finalize = gtk_cell_editable_widget_finalize; + object_class->dispose = gtk_cell_editable_widget_dispose; + object_class->set_property = gtk_cell_editable_widget_set_property; + object_class->get_property = gtk_cell_editable_widget_get_property; + + widget_class->unrealize = gtk_cell_editable_widget_unrealize; + + g_object_class_override_property (object_class, + PROP_EDITING_CANCELED, + "editing-canceled"); + + g_object_class_install_property (object_class, PROP_MODE, + g_param_spec_enum ("accel-mode", NULL, NULL, + GTK_TYPE_CELL_RENDERER_ACCEL_MODE, + GTK_CELL_RENDERER_ACCEL_MODE_GTK, + GTK_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_PATH, + g_param_spec_string ("path", NULL, NULL, + NULL, GTK_PARAM_READWRITE)); + + gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); + gtk_widget_class_set_css_name (widget_class, I_("acceleditor")); +} + +static void +gtk_cell_editable_widget_init (GtkCellEditableWidget *box) +{ + GtkWidget *widget = GTK_WIDGET (box); + GtkEventController *controller; + + gtk_widget_set_focusable (widget, TRUE); + + controller = gtk_event_controller_key_new (); + g_signal_connect (controller, "key-pressed", + G_CALLBACK (key_controller_key_pressed), box); + g_signal_connect (controller, "modifiers", + G_CALLBACK (key_controller_modifiers), box); + gtk_widget_add_controller (widget, controller); +} + +static GtkWidget * +gtk_cell_editable_widget_new (GtkCellRenderer *cell, + GtkCellRendererAccelMode mode, + const char *path) +{ + GtkCellEditableWidget *box; + + box = g_object_new (gtk_cell_editable_widget_get_type (), + "accel-mode", mode, + "path", path, + NULL); + box->cell = cell; + + box->label = gtk_label_new (NULL); + gtk_widget_set_halign (box->label, GTK_ALIGN_START); + gtk_widget_set_valign (box->label, GTK_ALIGN_CENTER); + + gtk_widget_set_state_flags (box->label, GTK_STATE_FLAG_SELECTED, TRUE); + + /* This label is displayed in a treeview cell displaying an accelerator + * when the cell is clicked to change the acelerator. + */ + gtk_label_set_text (GTK_LABEL (box->label), _("New accelerator…")); + + gtk_widget_set_parent (box->label, GTK_WIDGET (box)); + + gtk_grab_add (GTK_WIDGET (box)); + + return GTK_WIDGET (box); +} diff --git a/gtk/deprecated/gtkcellrendereraccel.h b/gtk/deprecated/gtkcellrendereraccel.h new file mode 100644 index 0000000000..df3abcdd71 --- /dev/null +++ b/gtk/deprecated/gtkcellrendereraccel.h @@ -0,0 +1,59 @@ +/* gtkcellrendereraccel.h + * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#ifndef __GTK_CELL_RENDERER_ACCEL_H__ +#define __GTK_CELL_RENDERER_ACCEL_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_CELL_RENDERER_ACCEL (gtk_cell_renderer_accel_get_type ()) +#define GTK_CELL_RENDERER_ACCEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_RENDERER_ACCEL, GtkCellRendererAccel)) +#define GTK_IS_CELL_RENDERER_ACCEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_RENDERER_ACCEL)) + +typedef struct _GtkCellRendererAccel GtkCellRendererAccel; + +/** + * GtkCellRendererAccelMode: + * @GTK_CELL_RENDERER_ACCEL_MODE_GTK: GTK accelerators mode + * @GTK_CELL_RENDERER_ACCEL_MODE_OTHER: Other accelerator mode + * + * The available modes for [property@Gtk.CellRendererAccel:accel-mode]. + */ +typedef enum +{ + GTK_CELL_RENDERER_ACCEL_MODE_GTK, + GTK_CELL_RENDERER_ACCEL_MODE_OTHER +} GtkCellRendererAccelMode; + + +GDK_AVAILABLE_IN_ALL +GType gtk_cell_renderer_accel_get_type (void) G_GNUC_CONST; +GDK_DEPRECATED_IN_4_10 +GtkCellRenderer *gtk_cell_renderer_accel_new (void); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkCellRendererAccel, g_object_unref) + +G_END_DECLS + + +#endif /* __GTK_CELL_RENDERER_ACCEL_H__ */ diff --git a/gtk/deprecated/gtkcellrenderercombo.c b/gtk/deprecated/gtkcellrenderercombo.c new file mode 100644 index 0000000000..73bb66f102 --- /dev/null +++ b/gtk/deprecated/gtkcellrenderercombo.c @@ -0,0 +1,513 @@ +/* GtkCellRendererCombo + * Copyright (C) 2004 Lorenzo Gil Sanchez + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#include "config.h" +#include + +#include "gtkentry.h" +#include "deprecated/gtkcelllayout.h" +#include "gtkcellrenderercombo.h" +#include "deprecated/gtkcellrenderertext.h" +#include "deprecated/gtkcombobox.h" +#include "gtkmarshalers.h" +#include "gtkprivate.h" + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + +/** + * GtkCellRendererCombo: + * + * Renders a combobox in a cell + * + * `GtkCellRendererCombo` renders text in a cell like `GtkCellRendererText` from + * which it is derived. But while `GtkCellRendererText` offers a simple entry to + * edit the text, `GtkCellRendererCombo` offers a `GtkComboBox` + * widget to edit the text. The values to display in the combo box are taken from + * the tree model specified in the `GtkCellRendererCombo`:model property. + * + * The combo cell renderer takes care of adding a text cell renderer to the combo + * box and sets it to display the column specified by its + * `GtkCellRendererCombo`:text-column property. Further properties of the combo box + * can be set in a handler for the `GtkCellRenderer::editing-started` signal. + */ + +typedef struct _GtkCellRendererComboPrivate GtkCellRendererComboPrivate; +typedef struct _GtkCellRendererComboClass GtkCellRendererComboClass; + +struct _GtkCellRendererCombo +{ + GtkCellRendererText parent; +}; + +struct _GtkCellRendererComboClass +{ + GtkCellRendererTextClass parent; +}; + + +struct _GtkCellRendererComboPrivate +{ + GtkTreeModel *model; + + GtkWidget *combo; + + gboolean has_entry; + + int text_column; + + gulong focus_out_id; +}; + + +static void gtk_cell_renderer_combo_finalize (GObject *object); +static void gtk_cell_renderer_combo_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); + +static void gtk_cell_renderer_combo_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); + +static GtkCellEditable *gtk_cell_renderer_combo_start_editing (GtkCellRenderer *cell, + GdkEvent *event, + GtkWidget *widget, + const char *path, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + GtkCellRendererState flags); + +enum { + PROP_0, + PROP_MODEL, + PROP_TEXT_COLUMN, + PROP_HAS_ENTRY +}; + +enum { + CHANGED, + LAST_SIGNAL +}; + +static guint cell_renderer_combo_signals[LAST_SIGNAL] = { 0, }; + +#define GTK_CELL_RENDERER_COMBO_PATH "gtk-cell-renderer-combo-path" + +G_DEFINE_TYPE_WITH_PRIVATE (GtkCellRendererCombo, gtk_cell_renderer_combo, GTK_TYPE_CELL_RENDERER_TEXT) + +static void +gtk_cell_renderer_combo_class_init (GtkCellRendererComboClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass); + + object_class->finalize = gtk_cell_renderer_combo_finalize; + object_class->get_property = gtk_cell_renderer_combo_get_property; + object_class->set_property = gtk_cell_renderer_combo_set_property; + + cell_class->start_editing = gtk_cell_renderer_combo_start_editing; + + /** + * GtkCellRendererCombo:model: + * + * Holds a tree model containing the possible values for the combo box. + * Use the text_column property to specify the column holding the values. + */ + g_object_class_install_property (object_class, + PROP_MODEL, + g_param_spec_object ("model", NULL, NULL, + GTK_TYPE_TREE_MODEL, + GTK_PARAM_READWRITE)); + + /** + * GtkCellRendererCombo:text-column: + * + * Specifies the model column which holds the possible values for the + * combo box. + * + * Note that this refers to the model specified in the model property, + * not the model backing the tree view to which + * this cell renderer is attached. + * + * `GtkCellRendererCombo` automatically adds a text cell renderer for + * this column to its combo box. + */ + g_object_class_install_property (object_class, + PROP_TEXT_COLUMN, + g_param_spec_int ("text-column", NULL, NULL, + -1, + G_MAXINT, + -1, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + /** + * GtkCellRendererCombo:has-entry: + * + * If %TRUE, the cell renderer will include an entry and allow to enter + * values other than the ones in the popup list. + */ + g_object_class_install_property (object_class, + PROP_HAS_ENTRY, + g_param_spec_boolean ("has-entry", NULL, NULL, + TRUE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + + /** + * GtkCellRendererCombo::changed: + * @combo: the object on which the signal is emitted + * @path_string: a string of the path identifying the edited cell + * (relative to the tree view model) + * @new_iter: the new iter selected in the combo box + * (relative to the combo box model) + * + * This signal is emitted each time after the user selected an item in + * the combo box, either by using the mouse or the arrow keys. Contrary + * to GtkComboBox, GtkCellRendererCombo::changed is not emitted for + * changes made to a selected item in the entry. The argument @new_iter + * corresponds to the newly selected item in the combo box and it is relative + * to the GtkTreeModel set via the model property on GtkCellRendererCombo. + * + * Note that as soon as you change the model displayed in the tree view, + * the tree view will immediately cease the editing operating. This + * means that you most probably want to refrain from changing the model + * until the combo cell renderer emits the edited or editing_canceled signal. + */ + cell_renderer_combo_signals[CHANGED] = + g_signal_new (I_("changed"), + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + _gtk_marshal_VOID__STRING_BOXED, + G_TYPE_NONE, 2, + G_TYPE_STRING, + GTK_TYPE_TREE_ITER); + g_signal_set_va_marshaller (cell_renderer_combo_signals[CHANGED], + G_TYPE_FROM_CLASS (object_class), + _gtk_marshal_VOID__STRING_BOXEDv); +} + +static void +gtk_cell_renderer_combo_init (GtkCellRendererCombo *self) +{ + GtkCellRendererComboPrivate *priv = gtk_cell_renderer_combo_get_instance_private (self); + + priv->model = NULL; + priv->text_column = -1; + priv->has_entry = TRUE; + priv->focus_out_id = 0; +} + +/** + * gtk_cell_renderer_combo_new: + * + * Creates a new `GtkCellRendererCombo`. + * Adjust how text is drawn using object properties. + * Object properties can be set globally (with g_object_set()). + * Also, with `GtkTreeViewColumn`, you can bind a property to a value + * in a `GtkTreeModel`. For example, you can bind the “text” property + * on the cell renderer to a string value in the model, thus rendering + * a different string in each row of the `GtkTreeView`. + * + * Returns: the new cell renderer + * + * Deprecated: 4.10 + */ +GtkCellRenderer * +gtk_cell_renderer_combo_new (void) +{ + return g_object_new (GTK_TYPE_CELL_RENDERER_COMBO, NULL); +} + +static void +gtk_cell_renderer_combo_finalize (GObject *object) +{ + GtkCellRendererCombo *cell = GTK_CELL_RENDERER_COMBO (object); + GtkCellRendererComboPrivate *priv = gtk_cell_renderer_combo_get_instance_private (cell); + + if (priv->model) + { + g_object_unref (priv->model); + priv->model = NULL; + } + + G_OBJECT_CLASS (gtk_cell_renderer_combo_parent_class)->finalize (object); +} + +static void +gtk_cell_renderer_combo_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkCellRendererCombo *cell = GTK_CELL_RENDERER_COMBO (object); + GtkCellRendererComboPrivate *priv = gtk_cell_renderer_combo_get_instance_private (cell); + + switch (prop_id) + { + case PROP_MODEL: + g_value_set_object (value, priv->model); + break; + case PROP_TEXT_COLUMN: + g_value_set_int (value, priv->text_column); + break; + case PROP_HAS_ENTRY: + g_value_set_boolean (value, priv->has_entry); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_cell_renderer_combo_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkCellRendererCombo *cell = GTK_CELL_RENDERER_COMBO (object); + GtkCellRendererComboPrivate *priv = gtk_cell_renderer_combo_get_instance_private (cell); + + switch (prop_id) + { + case PROP_MODEL: + { + if (priv->model) + g_object_unref (priv->model); + priv->model = GTK_TREE_MODEL (g_value_get_object (value)); + if (priv->model) + g_object_ref (priv->model); + break; + } + case PROP_TEXT_COLUMN: + if (priv->text_column != g_value_get_int (value)) + { + priv->text_column = g_value_get_int (value); + g_object_notify_by_pspec (object, pspec); + } + break; + case PROP_HAS_ENTRY: + if (priv->has_entry != g_value_get_boolean (value)) + { + priv->has_entry = g_value_get_boolean (value); + g_object_notify_by_pspec (object, pspec); + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_cell_renderer_combo_changed (GtkComboBox *combo, + gpointer data) +{ + GtkTreeIter iter; + GtkCellRendererCombo *cell; + + cell = GTK_CELL_RENDERER_COMBO (data); + + if (gtk_combo_box_get_active_iter (combo, &iter)) + { + const char *path; + + path = g_object_get_data (G_OBJECT (combo), GTK_CELL_RENDERER_COMBO_PATH); + g_signal_emit (cell, cell_renderer_combo_signals[CHANGED], 0, + path, &iter); + } +} + +static void +gtk_cell_renderer_combo_editing_done (GtkCellEditable *combo, + gpointer data) +{ + GtkCellRendererCombo *cell = GTK_CELL_RENDERER_COMBO (data); + GtkCellRendererComboPrivate *priv = gtk_cell_renderer_combo_get_instance_private (cell); + const char *path; + char *new_text = NULL; + GtkTreeModel *model; + GtkTreeIter iter; + GtkEntry *entry; + gboolean canceled; + + if (priv->focus_out_id > 0) + { + g_signal_handler_disconnect (combo, priv->focus_out_id); + priv->focus_out_id = 0; + } + + g_object_get (combo, + "editing-canceled", &canceled, + NULL); + gtk_cell_renderer_stop_editing (GTK_CELL_RENDERER (data), canceled); + if (canceled) + { + priv->combo = NULL; + return; + } + + if (gtk_combo_box_get_has_entry (GTK_COMBO_BOX (combo))) + { + entry = GTK_ENTRY (gtk_combo_box_get_child (GTK_COMBO_BOX (combo))); + new_text = g_strdup (gtk_editable_get_text (GTK_EDITABLE (entry))); + } + else + { + model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo)); + + if (model + && gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter)) + gtk_tree_model_get (model, &iter, priv->text_column, &new_text, -1); + } + + path = g_object_get_data (G_OBJECT (combo), GTK_CELL_RENDERER_COMBO_PATH); + g_signal_emit_by_name (cell, "edited", path, new_text); + + priv->combo = NULL; + + g_free (new_text); +} + +static void +gtk_cell_renderer_combo_focus_change (GtkWidget *widget, + GParamSpec *pspec, + gpointer data) +{ + if (!gtk_widget_has_focus (widget)) + gtk_cell_renderer_combo_editing_done (GTK_CELL_EDITABLE (widget), data); +} + +typedef struct +{ + GtkCellRendererCombo *cell; + gboolean found; + GtkTreeIter iter; +} SearchData; + +static gboolean +find_text (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + SearchData *search_data = (SearchData *)data; + GtkCellRendererComboPrivate *priv = gtk_cell_renderer_combo_get_instance_private (search_data->cell); + char *text, *cell_text; + + gtk_tree_model_get (model, iter, priv->text_column, &text, -1); + g_object_get (GTK_CELL_RENDERER_TEXT (search_data->cell), + "text", &cell_text, + NULL); + if (text && cell_text && g_strcmp0 (text, cell_text) == 0) + { + search_data->iter = *iter; + search_data->found = TRUE; + } + + g_free (cell_text); + g_free (text); + + return search_data->found; +} + +static GtkCellEditable * +gtk_cell_renderer_combo_start_editing (GtkCellRenderer *cell, + GdkEvent *event, + GtkWidget *widget, + const char *path, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + GtkCellRendererState flags) +{ + GtkCellRendererCombo *cell_combo = GTK_CELL_RENDERER_COMBO (cell); + GtkCellRendererComboPrivate *priv = gtk_cell_renderer_combo_get_instance_private (cell_combo); + GtkWidget *combo; + SearchData search_data; + gboolean editable; + char *text; + + g_object_get (cell, "editable", &editable, NULL); + if (editable == FALSE) + return NULL; + + if (priv->text_column < 0) + return NULL; + + if (priv->has_entry) + { + combo = g_object_new (GTK_TYPE_COMBO_BOX, "has-entry", TRUE, NULL); + + if (priv->model) + gtk_combo_box_set_model (GTK_COMBO_BOX (combo), priv->model); + gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX (combo), + priv->text_column); + + g_object_get (cell, "text", &text, NULL); + if (text) + gtk_editable_set_text (GTK_EDITABLE (gtk_combo_box_get_child (GTK_COMBO_BOX (combo))), text); + g_free (text); + } + else + { + cell = gtk_cell_renderer_text_new (); + + combo = gtk_combo_box_new (); + if (priv->model) + gtk_combo_box_set_model (GTK_COMBO_BOX (combo), priv->model); + + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), + cell, "text", priv->text_column, + NULL); + + /* determine the current value */ + if (priv->model) + { + search_data.cell = cell_combo; + search_data.found = FALSE; + gtk_tree_model_foreach (priv->model, find_text, &search_data); + if (search_data.found) + gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo), + &(search_data.iter)); + } + } + + g_object_set (combo, "has-frame", FALSE, NULL); + g_object_set_data_full (G_OBJECT (combo), + I_(GTK_CELL_RENDERER_COMBO_PATH), + g_strdup (path), g_free); + + gtk_widget_show (combo); + + g_signal_connect (GTK_CELL_EDITABLE (combo), "editing-done", + G_CALLBACK (gtk_cell_renderer_combo_editing_done), + cell_combo); + g_signal_connect (GTK_CELL_EDITABLE (combo), "changed", + G_CALLBACK (gtk_cell_renderer_combo_changed), + cell_combo); + priv->focus_out_id = g_signal_connect (combo, "notify::has-focus", + G_CALLBACK (gtk_cell_renderer_combo_focus_change), + cell_combo); + + priv->combo = combo; + + return GTK_CELL_EDITABLE (combo); +} diff --git a/gtk/deprecated/gtkcellrenderercombo.h b/gtk/deprecated/gtkcellrenderercombo.h new file mode 100644 index 0000000000..5ae2a60303 --- /dev/null +++ b/gtk/deprecated/gtkcellrenderercombo.h @@ -0,0 +1,45 @@ +/* GtkCellRendererCombo + * Copyright (C) 2004 Lorenzo Gil Sanchez + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#ifndef __GTK_CELL_RENDERER_COMBO_H__ +#define __GTK_CELL_RENDERER_COMBO_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_CELL_RENDERER_COMBO (gtk_cell_renderer_combo_get_type ()) +#define GTK_CELL_RENDERER_COMBO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_RENDERER_COMBO, GtkCellRendererCombo)) +#define GTK_IS_CELL_RENDERER_COMBO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_RENDERER_COMBO)) + +typedef struct _GtkCellRendererCombo GtkCellRendererCombo; + +GDK_AVAILABLE_IN_ALL +GType gtk_cell_renderer_combo_get_type (void) G_GNUC_CONST; +GDK_DEPRECATED_IN_4_10 +GtkCellRenderer *gtk_cell_renderer_combo_new (void); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkCellRendererCombo, g_object_unref) + +G_END_DECLS + +#endif /* __GTK_CELL_RENDERER_COMBO_H__ */ diff --git a/gtk/deprecated/gtkcellrendererpixbuf.c b/gtk/deprecated/gtkcellrendererpixbuf.c new file mode 100644 index 0000000000..ff62bd976e --- /dev/null +++ b/gtk/deprecated/gtkcellrendererpixbuf.c @@ -0,0 +1,595 @@ +/* gtkcellrendererpixbuf.c + * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#include "config.h" + +#include "gtkcellrendererpixbuf.h" + +#include "gtkiconhelperprivate.h" +#include "gtkicontheme.h" +#include "gtkprivate.h" +#include "gtksnapshot.h" +#include "gtkstylecontextprivate.h" +#include "gtktypebuiltins.h" + +#include +#include + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + +/** + * GtkCellRendererPixbuf: + * + * Renders a pixbuf in a cell + * + * A `GtkCellRendererPixbuf` can be used to render an image in a cell. It allows + * to render either a given `GdkPixbuf` (set via the + * `GtkCellRendererPixbuf:pixbuf` property) or a named icon (set via the + * `GtkCellRendererPixbuf:icon-name` property). + * + * To support the tree view, `GtkCellRendererPixbuf` also supports rendering two + * alternative pixbufs, when the `GtkCellRenderer:is-expander` property is %TRUE. + * If the `GtkCellRenderer:is-expanded property` is %TRUE and the + * `GtkCellRendererPixbuf:pixbuf-expander-open` property is set to a pixbuf, it + * renders that pixbuf, if the `GtkCellRenderer:is-expanded` property is %FALSE + * and the `GtkCellRendererPixbuf:pixbuf-expander-closed` property is set to a + * pixbuf, it renders that one. + */ + + +static void gtk_cell_renderer_pixbuf_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void gtk_cell_renderer_pixbuf_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); +static void gtk_cell_renderer_pixbuf_get_size (GtkCellRendererPixbuf *self, + GtkWidget *widget, + const GdkRectangle *rectangle, + int *x_offset, + int *y_offset, + int *width, + int *height); +static void gtk_cell_renderer_pixbuf_snapshot (GtkCellRenderer *cell, + GtkSnapshot *snapshot, + GtkWidget *widget, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + GtkCellRendererState flags); + + +enum { + PROP_0, + PROP_PIXBUF, + PROP_PIXBUF_EXPANDER_OPEN, + PROP_PIXBUF_EXPANDER_CLOSED, + PROP_TEXTURE, + PROP_ICON_SIZE, + PROP_ICON_NAME, + PROP_GICON +}; + +typedef struct _GtkCellRendererPixbufPrivate GtkCellRendererPixbufPrivate; +typedef struct _GtkCellRendererPixbufClass GtkCellRendererPixbufClass; + +struct _GtkCellRendererPixbuf +{ + GtkCellRenderer parent; +}; + +struct _GtkCellRendererPixbufClass +{ + GtkCellRendererClass parent_class; +}; + +struct _GtkCellRendererPixbufPrivate +{ + GtkImageDefinition *image_def; + GtkIconSize icon_size; + + GdkPixbuf *pixbuf_expander_open; + GdkPixbuf *pixbuf_expander_closed; + GdkTexture *texture_expander_open; + GdkTexture *texture_expander_closed; +}; + +G_DEFINE_TYPE_WITH_PRIVATE (GtkCellRendererPixbuf, gtk_cell_renderer_pixbuf, GTK_TYPE_CELL_RENDERER) + +static void +gtk_cell_renderer_pixbuf_init (GtkCellRendererPixbuf *cellpixbuf) +{ + GtkCellRendererPixbufPrivate *priv = gtk_cell_renderer_pixbuf_get_instance_private (cellpixbuf); + + priv->image_def = gtk_image_definition_new_empty (); +} + +static void +gtk_cell_renderer_pixbuf_finalize (GObject *object) +{ + GtkCellRendererPixbuf *cellpixbuf = GTK_CELL_RENDERER_PIXBUF (object); + GtkCellRendererPixbufPrivate *priv = gtk_cell_renderer_pixbuf_get_instance_private (cellpixbuf); + + gtk_image_definition_unref (priv->image_def); + + g_clear_object (&priv->pixbuf_expander_open); + g_clear_object (&priv->pixbuf_expander_closed); + g_clear_object (&priv->texture_expander_open); + g_clear_object (&priv->texture_expander_closed); + + G_OBJECT_CLASS (gtk_cell_renderer_pixbuf_parent_class)->finalize (object); +} + +static GtkSizeRequestMode +gtk_cell_renderer_pixbuf_get_request_mode (GtkCellRenderer *cell) +{ + return GTK_SIZE_REQUEST_CONSTANT_SIZE; +} + +static void +gtk_cell_renderer_pixbuf_get_preferred_width (GtkCellRenderer *cell, + GtkWidget *widget, + int *minimum, + int *natural) +{ + int size = 0; + + gtk_cell_renderer_pixbuf_get_size (GTK_CELL_RENDERER_PIXBUF (cell), widget, NULL, + NULL, NULL, &size, NULL); + + if (minimum != NULL) + *minimum = size; + + if (natural != NULL) + *natural = size; +} + +static void +gtk_cell_renderer_pixbuf_get_preferred_height (GtkCellRenderer *cell, + GtkWidget *widget, + int *minimum, + int *natural) +{ + int size = 0; + + gtk_cell_renderer_pixbuf_get_size (GTK_CELL_RENDERER_PIXBUF (cell), widget, NULL, + NULL, NULL, NULL, &size); + + if (minimum != NULL) + *minimum = size; + + if (natural != NULL) + *natural = size; +} + +static void +gtk_cell_renderer_pixbuf_class_init (GtkCellRendererPixbufClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (class); + + object_class->finalize = gtk_cell_renderer_pixbuf_finalize; + + object_class->get_property = gtk_cell_renderer_pixbuf_get_property; + object_class->set_property = gtk_cell_renderer_pixbuf_set_property; + + cell_class->get_request_mode = gtk_cell_renderer_pixbuf_get_request_mode; + cell_class->get_preferred_width = gtk_cell_renderer_pixbuf_get_preferred_width; + cell_class->get_preferred_height = gtk_cell_renderer_pixbuf_get_preferred_height; + cell_class->snapshot = gtk_cell_renderer_pixbuf_snapshot; + + g_object_class_install_property (object_class, + PROP_PIXBUF, + g_param_spec_object ("pixbuf", NULL, NULL, + GDK_TYPE_PIXBUF, + GTK_PARAM_WRITABLE)); + + g_object_class_install_property (object_class, + PROP_PIXBUF_EXPANDER_OPEN, + g_param_spec_object ("pixbuf-expander-open", NULL, NULL, + GDK_TYPE_PIXBUF, + GTK_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_PIXBUF_EXPANDER_CLOSED, + g_param_spec_object ("pixbuf-expander-closed", NULL, NULL, + GDK_TYPE_PIXBUF, + GTK_PARAM_READWRITE)); + + /** + * GtkCellRendererPixbuf:texture: + */ + g_object_class_install_property (object_class, + PROP_TEXTURE, + g_param_spec_object ("texture", NULL, NULL, + GDK_TYPE_TEXTURE, + GTK_PARAM_READWRITE)); + + /** + * GtkCellRendererPixbuf:icon-size: + * + * The `GtkIconSize` value that specifies the size of the rendered icon. + */ + g_object_class_install_property (object_class, + PROP_ICON_SIZE, + g_param_spec_enum ("icon-size", NULL, NULL, + GTK_TYPE_ICON_SIZE, + GTK_ICON_SIZE_INHERIT, + GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY)); + + /** + * GtkCellRendererPixbuf:icon-name: + * + * The name of the themed icon to display. + * This property only has an effect if not overridden by the "pixbuf" property. + */ + g_object_class_install_property (object_class, + PROP_ICON_NAME, + g_param_spec_string ("icon-name", NULL, NULL, + NULL, + GTK_PARAM_READWRITE)); + + /** + * GtkCellRendererPixbuf:gicon: + * + * The GIcon representing the icon to display. + * If the icon theme is changed, the image will be updated + * automatically. + */ + g_object_class_install_property (object_class, + PROP_GICON, + g_param_spec_object ("gicon", NULL, NULL, + G_TYPE_ICON, + GTK_PARAM_READWRITE)); +} + +static void +gtk_cell_renderer_pixbuf_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + GtkCellRendererPixbuf *cellpixbuf = GTK_CELL_RENDERER_PIXBUF (object); + GtkCellRendererPixbufPrivate *priv = gtk_cell_renderer_pixbuf_get_instance_private (cellpixbuf); + + switch (param_id) + { + case PROP_PIXBUF_EXPANDER_OPEN: + g_value_set_object (value, priv->pixbuf_expander_open); + break; + case PROP_PIXBUF_EXPANDER_CLOSED: + g_value_set_object (value, priv->pixbuf_expander_closed); + break; + case PROP_TEXTURE: + g_value_set_object (value, gtk_image_definition_get_paintable (priv->image_def)); + break; + case PROP_ICON_SIZE: + g_value_set_enum (value, priv->icon_size); + break; + case PROP_ICON_NAME: + g_value_set_string (value, gtk_image_definition_get_icon_name (priv->image_def)); + break; + case PROP_GICON: + g_value_set_object (value, gtk_image_definition_get_gicon (priv->image_def)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +notify_storage_type (GtkCellRendererPixbuf *cellpixbuf, + GtkImageType storage_type) +{ + switch (storage_type) + { + case GTK_IMAGE_PAINTABLE: + g_object_notify (G_OBJECT (cellpixbuf), "texture"); + break; + case GTK_IMAGE_ICON_NAME: + g_object_notify (G_OBJECT (cellpixbuf), "icon-name"); + break; + case GTK_IMAGE_GICON: + g_object_notify (G_OBJECT (cellpixbuf), "gicon"); + break; + default: + g_assert_not_reached (); + case GTK_IMAGE_EMPTY: + break; + } +} + +static void +take_image_definition (GtkCellRendererPixbuf *cellpixbuf, + GtkImageDefinition *def) +{ + GtkCellRendererPixbufPrivate *priv = gtk_cell_renderer_pixbuf_get_instance_private (cellpixbuf); + GtkImageType old_storage_type, new_storage_type; + + if (def == NULL) + def = gtk_image_definition_new_empty (); + + old_storage_type = gtk_image_definition_get_storage_type (priv->image_def); + new_storage_type = gtk_image_definition_get_storage_type (def); + + if (new_storage_type != old_storage_type) + notify_storage_type (cellpixbuf, old_storage_type); + + gtk_image_definition_unref (priv->image_def); + priv->image_def = def; +} + +static void +gtk_cell_renderer_pixbuf_set_icon_size (GtkCellRendererPixbuf *cellpixbuf, + GtkIconSize icon_size) +{ + GtkCellRendererPixbufPrivate *priv = gtk_cell_renderer_pixbuf_get_instance_private (cellpixbuf); + + if (priv->icon_size == icon_size) + return; + + priv->icon_size = icon_size; + g_object_notify (G_OBJECT (cellpixbuf), "icon-size"); +} + +static void +gtk_cell_renderer_pixbuf_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkCellRendererPixbuf *cellpixbuf = GTK_CELL_RENDERER_PIXBUF (object); + GtkCellRendererPixbufPrivate *priv = gtk_cell_renderer_pixbuf_get_instance_private (cellpixbuf); + GdkTexture *texture; + GdkPixbuf *pixbuf; + + switch (param_id) + { + case PROP_PIXBUF: + pixbuf = g_value_get_object (value); + if (pixbuf) + texture = gdk_texture_new_for_pixbuf (pixbuf); + else + texture = NULL; + take_image_definition (cellpixbuf, gtk_image_definition_new_paintable (GDK_PAINTABLE (texture))); + break; + case PROP_PIXBUF_EXPANDER_OPEN: + g_clear_object (&priv->pixbuf_expander_open); + g_clear_object (&priv->texture_expander_open); + priv->pixbuf_expander_open = (GdkPixbuf*) g_value_dup_object (value); + priv->texture_expander_open = gdk_texture_new_for_pixbuf (priv->pixbuf_expander_open); + break; + case PROP_PIXBUF_EXPANDER_CLOSED: + g_clear_object (&priv->pixbuf_expander_closed); + g_clear_object (&priv->texture_expander_closed); + priv->pixbuf_expander_closed = (GdkPixbuf*) g_value_dup_object (value); + priv->texture_expander_closed = gdk_texture_new_for_pixbuf (priv->pixbuf_expander_open); + break; + case PROP_TEXTURE: + take_image_definition (cellpixbuf, gtk_image_definition_new_paintable (g_value_get_object (value))); + break; + case PROP_ICON_SIZE: + gtk_cell_renderer_pixbuf_set_icon_size (cellpixbuf, g_value_get_enum (value)); + break; + case PROP_ICON_NAME: + take_image_definition (cellpixbuf, gtk_image_definition_new_icon_name (g_value_get_string (value))); + break; + case PROP_GICON: + take_image_definition (cellpixbuf, gtk_image_definition_new_gicon (g_value_get_object (value))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +/** + * gtk_cell_renderer_pixbuf_new: + * + * Creates a new `GtkCellRendererPixbuf`. Adjust rendering + * parameters using object properties. Object properties can be set + * globally (with g_object_set()). Also, with `GtkTreeViewColumn`, you + * can bind a property to a value in a `GtkTreeModel`. For example, you + * can bind the “pixbuf” property on the cell renderer to a pixbuf value + * in the model, thus rendering a different image in each row of the + * `GtkTreeView`. + * + * Returns: the new cell renderer + * + * Deprecated: 4.10 + **/ +GtkCellRenderer * +gtk_cell_renderer_pixbuf_new (void) +{ + return g_object_new (GTK_TYPE_CELL_RENDERER_PIXBUF, NULL); +} + +static GtkIconHelper * +create_icon_helper (GtkCellRendererPixbuf *cellpixbuf, + GtkWidget *widget) +{ + GtkCellRendererPixbufPrivate *priv = gtk_cell_renderer_pixbuf_get_instance_private (cellpixbuf); + GtkIconHelper *icon_helper; + + icon_helper = gtk_icon_helper_new (gtk_style_context_get_node (gtk_widget_get_style_context (widget)), + widget); + _gtk_icon_helper_set_use_fallback (icon_helper, TRUE); + _gtk_icon_helper_set_definition (icon_helper, priv->image_def); + + return icon_helper; +} + +static void +gtk_cell_renderer_pixbuf_get_size (GtkCellRendererPixbuf *self, + GtkWidget *widget, + const GdkRectangle *cell_area, + int *x_offset, + int *y_offset, + int *width, + int *height) +{ + GtkCellRendererPixbufPrivate *priv = gtk_cell_renderer_pixbuf_get_instance_private (self); + GtkCellRenderer *cell = GTK_CELL_RENDERER (self); + int pixbuf_width; + int pixbuf_height; + int calc_width; + int calc_height; + int xpad, ypad; + GtkStyleContext *context; + GtkIconHelper *icon_helper; + + context = gtk_widget_get_style_context (widget); + gtk_style_context_save (context); + gtk_style_context_add_class (context, "image"); + gtk_icon_size_set_style_classes (gtk_style_context_get_node (context), priv->icon_size); + icon_helper = create_icon_helper (self, widget); + + if (_gtk_icon_helper_get_is_empty (icon_helper)) + pixbuf_width = pixbuf_height = 0; + else if (gtk_image_definition_get_paintable (priv->image_def)) + { + GdkPaintable *paintable = gtk_image_definition_get_paintable (priv->image_def); + pixbuf_width = gdk_paintable_get_intrinsic_width (paintable); + pixbuf_height = gdk_paintable_get_intrinsic_height (paintable); + } + else + pixbuf_width = pixbuf_height = gtk_icon_helper_get_size (icon_helper); + + g_object_unref (icon_helper); + gtk_style_context_restore (context); + + if (priv->pixbuf_expander_open) + { + pixbuf_width = MAX (pixbuf_width, gdk_pixbuf_get_width (priv->pixbuf_expander_open)); + pixbuf_height = MAX (pixbuf_height, gdk_pixbuf_get_height (priv->pixbuf_expander_open)); + } + if (priv->pixbuf_expander_closed) + { + pixbuf_width = MAX (pixbuf_width, gdk_pixbuf_get_width (priv->pixbuf_expander_closed)); + pixbuf_height = MAX (pixbuf_height, gdk_pixbuf_get_height (priv->pixbuf_expander_closed)); + } + + gtk_cell_renderer_get_padding (cell, &xpad, &ypad); + calc_width = (int) xpad * 2 + pixbuf_width; + calc_height = (int) ypad * 2 + pixbuf_height; + + if (cell_area && pixbuf_width > 0 && pixbuf_height > 0) + { + float xalign, yalign; + + gtk_cell_renderer_get_alignment (cell, &xalign, &yalign); + if (x_offset) + { + *x_offset = (((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) ? + (1.0 - xalign) : xalign) * + (cell_area->width - calc_width)); + *x_offset = MAX (*x_offset, 0); + } + if (y_offset) + { + *y_offset = (yalign * + (cell_area->height - calc_height)); + *y_offset = MAX (*y_offset, 0); + } + } + else + { + if (x_offset) *x_offset = 0; + if (y_offset) *y_offset = 0; + } + + if (width) + *width = calc_width; + + if (height) + *height = calc_height; +} + +static void +gtk_cell_renderer_pixbuf_snapshot (GtkCellRenderer *cell, + GtkSnapshot *snapshot, + GtkWidget *widget, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + GtkCellRendererState flags) + +{ + GtkCellRendererPixbuf *cellpixbuf = GTK_CELL_RENDERER_PIXBUF (cell); + GtkCellRendererPixbufPrivate *priv = gtk_cell_renderer_pixbuf_get_instance_private (cellpixbuf); + GtkStyleContext *context; + GdkRectangle pix_rect; + gboolean is_expander; + int xpad, ypad; + GtkIconHelper *icon_helper; + + gtk_cell_renderer_pixbuf_get_size (cellpixbuf, widget, + cell_area, + &pix_rect.x, + &pix_rect.y, + &pix_rect.width, + &pix_rect.height); + + gtk_cell_renderer_get_padding (cell, &xpad, &ypad); + pix_rect.x += cell_area->x + xpad; + pix_rect.y += cell_area->y + ypad; + pix_rect.width -= xpad * 2; + pix_rect.height -= ypad * 2; + + if (!gdk_rectangle_intersect (cell_area, &pix_rect, NULL)) + return; + + context = gtk_widget_get_style_context (widget); + gtk_style_context_save (context); + + gtk_style_context_add_class (context, "image"); + gtk_icon_size_set_style_classes (gtk_style_context_get_node (context), priv->icon_size); + + is_expander = gtk_cell_renderer_get_is_expander (cell); + if (is_expander) + { + gboolean is_expanded = gtk_cell_renderer_get_is_expanded (cell);; + + if (is_expanded && priv->pixbuf_expander_open != NULL) + { + icon_helper = gtk_icon_helper_new (gtk_style_context_get_node (context), widget); + _gtk_icon_helper_set_paintable (icon_helper, GDK_PAINTABLE (priv->texture_expander_open)); + } + else if (!is_expanded && priv->pixbuf_expander_closed != NULL) + { + icon_helper = gtk_icon_helper_new (gtk_style_context_get_node (context), widget); + _gtk_icon_helper_set_paintable (icon_helper, GDK_PAINTABLE (priv->texture_expander_closed)); + } + else + { + icon_helper = create_icon_helper (cellpixbuf, widget); + } + } + else + { + icon_helper = create_icon_helper (cellpixbuf, widget); + } + + gtk_snapshot_save (snapshot); + gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (pix_rect.x, pix_rect.y)); + gdk_paintable_snapshot (GDK_PAINTABLE (icon_helper), snapshot, pix_rect.width, pix_rect.height); + gtk_snapshot_restore (snapshot); + + g_object_unref (icon_helper); + gtk_style_context_restore (context); +} diff --git a/gtk/deprecated/gtkcellrendererpixbuf.h b/gtk/deprecated/gtkcellrendererpixbuf.h new file mode 100644 index 0000000000..3cee275218 --- /dev/null +++ b/gtk/deprecated/gtkcellrendererpixbuf.h @@ -0,0 +1,47 @@ +/* gtkcellrendererpixbuf.h + * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#ifndef __GTK_CELL_RENDERER_PIXBUF_H__ +#define __GTK_CELL_RENDERER_PIXBUF_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include + + +G_BEGIN_DECLS + + +#define GTK_TYPE_CELL_RENDERER_PIXBUF (gtk_cell_renderer_pixbuf_get_type ()) +#define GTK_CELL_RENDERER_PIXBUF(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_RENDERER_PIXBUF, GtkCellRendererPixbuf)) +#define GTK_IS_CELL_RENDERER_PIXBUF(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_RENDERER_PIXBUF)) + +typedef struct _GtkCellRendererPixbuf GtkCellRendererPixbuf; + +GDK_AVAILABLE_IN_ALL +GType gtk_cell_renderer_pixbuf_get_type (void) G_GNUC_CONST; +GDK_DEPRECATED_IN_4_10 +GtkCellRenderer *gtk_cell_renderer_pixbuf_new (void); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkCellRendererPixbuf, g_object_unref) + +G_END_DECLS + + +#endif /* __GTK_CELL_RENDERER_PIXBUF_H__ */ diff --git a/gtk/deprecated/gtkcellrendererprogress.c b/gtk/deprecated/gtkcellrendererprogress.c new file mode 100644 index 0000000000..be8c6edaad --- /dev/null +++ b/gtk/deprecated/gtkcellrendererprogress.c @@ -0,0 +1,729 @@ +/* gtkcellrendererprogress.c + * Copyright (C) 2002 Naba Kumar + * heavily modified by Jörgen Scheibengruber + * heavily modified by Marco Pesenti Gritti + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ +/* + * Modified by the GTK+ Team and others 1997-2007. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#include "config.h" +#include + +#include "gtkcellrendererprogress.h" +#include +#include "gtkorientable.h" +#include "gtkprivate.h" +#include "gtksnapshot.h" +#include "gtkstylecontext.h" + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + +/** + * GtkCellRendererProgress: + * + * Renders numbers as progress bars + * + * `GtkCellRendererProgress` renders a numeric value as a progress par in a cell. + * Additionally, it can display a text on top of the progress bar. + */ + + +enum +{ + PROP_0, + PROP_VALUE, + PROP_TEXT, + PROP_PULSE, + PROP_TEXT_XALIGN, + PROP_TEXT_YALIGN, + PROP_ORIENTATION, + PROP_INVERTED +}; + +typedef struct _GtkCellRendererProgressClass GtkCellRendererProgressClass; +typedef struct _GtkCellRendererProgressPrivate GtkCellRendererProgressPrivate; + +struct _GtkCellRendererProgress +{ + GtkCellRenderer parent_instance; +}; + +struct _GtkCellRendererProgressClass +{ + GtkCellRendererClass parent_class; +}; + +struct _GtkCellRendererProgressPrivate +{ + int value; + char *text; + char *label; + int min_h; + int min_w; + int pulse; + int offset; + float text_xalign; + float text_yalign; + GtkOrientation orientation; + gboolean inverted; +}; + +static void gtk_cell_renderer_progress_finalize (GObject *object); +static void gtk_cell_renderer_progress_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void gtk_cell_renderer_progress_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); +static void gtk_cell_renderer_progress_set_value (GtkCellRendererProgress *cellprogress, + int value); +static void gtk_cell_renderer_progress_set_text (GtkCellRendererProgress *cellprogress, + const char *text); +static void gtk_cell_renderer_progress_set_pulse (GtkCellRendererProgress *cellprogress, + int pulse); +static void compute_dimensions (GtkCellRenderer *cell, + GtkWidget *widget, + const char *text, + int *width, + int *height); +static void gtk_cell_renderer_progress_snapshot (GtkCellRenderer *cell, + GtkSnapshot *snapshot, + GtkWidget *widget, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + GtkCellRendererState flags); + + +G_DEFINE_TYPE_WITH_CODE (GtkCellRendererProgress, gtk_cell_renderer_progress, GTK_TYPE_CELL_RENDERER, + G_ADD_PRIVATE (GtkCellRendererProgress) + G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL)) + +static void +recompute_label (GtkCellRendererProgress *cellprogress) +{ + GtkCellRendererProgressPrivate *priv = gtk_cell_renderer_progress_get_instance_private (cellprogress); + char *label; + + if (priv->text) + label = g_strdup (priv->text); + else if (priv->pulse < 0) + label = g_strdup_printf (C_("progress bar label", "%d %%"), priv->value); + else + label = NULL; + + g_free (priv->label); + priv->label = label; +} + +static void +gtk_cell_renderer_progress_set_value (GtkCellRendererProgress *cellprogress, + int value) +{ + GtkCellRendererProgressPrivate *priv = gtk_cell_renderer_progress_get_instance_private (cellprogress); + + if (priv->value != value) + { + priv->value = value; + recompute_label (cellprogress); + g_object_notify (G_OBJECT (cellprogress), "value"); + } +} + +static void +gtk_cell_renderer_progress_set_text (GtkCellRendererProgress *cellprogress, + const char *text) +{ + GtkCellRendererProgressPrivate *priv = gtk_cell_renderer_progress_get_instance_private (cellprogress); + char *new_text; + + new_text = g_strdup (text); + g_free (priv->text); + priv->text = new_text; + recompute_label (cellprogress); + g_object_notify (G_OBJECT (cellprogress), "text"); +} + +static void +gtk_cell_renderer_progress_set_pulse (GtkCellRendererProgress *cellprogress, + int pulse) +{ + GtkCellRendererProgressPrivate *priv = gtk_cell_renderer_progress_get_instance_private (cellprogress); + + if (pulse != priv->pulse) + { + if (pulse <= 0) + priv->offset = 0; + else + priv->offset = pulse; + g_object_notify (G_OBJECT (cellprogress), "pulse"); + } + + priv->pulse = pulse; + recompute_label (cellprogress); +} + +static void +gtk_cell_renderer_progress_finalize (GObject *object) +{ + GtkCellRendererProgress *cellprogress = GTK_CELL_RENDERER_PROGRESS (object); + GtkCellRendererProgressPrivate *priv = gtk_cell_renderer_progress_get_instance_private (cellprogress); + + g_free (priv->text); + g_free (priv->label); + + G_OBJECT_CLASS (gtk_cell_renderer_progress_parent_class)->finalize (object); +} + +static void +gtk_cell_renderer_progress_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + GtkCellRendererProgress *cellprogress = GTK_CELL_RENDERER_PROGRESS (object); + GtkCellRendererProgressPrivate *priv = gtk_cell_renderer_progress_get_instance_private (cellprogress); + + switch (param_id) + { + case PROP_VALUE: + g_value_set_int (value, priv->value); + break; + case PROP_TEXT: + g_value_set_string (value, priv->text); + break; + case PROP_PULSE: + g_value_set_int (value, priv->pulse); + break; + case PROP_TEXT_XALIGN: + g_value_set_float (value, priv->text_xalign); + break; + case PROP_TEXT_YALIGN: + g_value_set_float (value, priv->text_yalign); + break; + case PROP_ORIENTATION: + g_value_set_enum (value, priv->orientation); + break; + case PROP_INVERTED: + g_value_set_boolean (value, priv->inverted); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + } +} + +static void +gtk_cell_renderer_progress_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkCellRendererProgress *cellprogress = GTK_CELL_RENDERER_PROGRESS (object); + GtkCellRendererProgressPrivate *priv = gtk_cell_renderer_progress_get_instance_private (cellprogress); + + switch (param_id) + { + case PROP_VALUE: + gtk_cell_renderer_progress_set_value (cellprogress, + g_value_get_int (value)); + break; + case PROP_TEXT: + gtk_cell_renderer_progress_set_text (cellprogress, + g_value_get_string (value)); + break; + case PROP_PULSE: + gtk_cell_renderer_progress_set_pulse (cellprogress, + g_value_get_int (value)); + break; + case PROP_TEXT_XALIGN: + priv->text_xalign = g_value_get_float (value); + break; + case PROP_TEXT_YALIGN: + priv->text_yalign = g_value_get_float (value); + break; + case PROP_ORIENTATION: + if (priv->orientation != g_value_get_enum (value)) + { + priv->orientation = g_value_get_enum (value); + g_object_notify_by_pspec (object, pspec); + } + break; + case PROP_INVERTED: + if (priv->inverted != g_value_get_boolean (value)) + { + priv->inverted = g_value_get_boolean (value); + g_object_notify_by_pspec (object, pspec); + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + } +} + +static void +compute_dimensions (GtkCellRenderer *cell, + GtkWidget *widget, + const char *text, + int *width, + int *height) +{ + PangoRectangle logical_rect; + PangoLayout *layout; + int xpad, ypad; + + layout = gtk_widget_create_pango_layout (widget, text); + pango_layout_get_pixel_extents (layout, NULL, &logical_rect); + + gtk_cell_renderer_get_padding (cell, &xpad, &ypad); + + if (width) + *width = logical_rect.width + xpad * 2; + + if (height) + *height = logical_rect.height + ypad * 2; + + g_object_unref (layout); +} + +static void +gtk_cell_renderer_progress_get_preferred_width (GtkCellRenderer *cell, + GtkWidget *widget, + int *minimum, + int *natural) +{ + GtkCellRendererProgress *self = GTK_CELL_RENDERER_PROGRESS (cell); + GtkCellRendererProgressPrivate *priv = gtk_cell_renderer_progress_get_instance_private (self); + int w, h; + int size; + + if (priv->min_w < 0) + { + char *text = g_strdup_printf (C_("progress bar label", "%d %%"), 100); + compute_dimensions (cell, widget, text, + &priv->min_w, + &priv->min_h); + g_free (text); + } + + compute_dimensions (cell, widget, priv->label, &w, &h); + + size = MAX (priv->min_w, w); + + if (minimum != NULL) + *minimum = size; + if (natural != NULL) + *natural = size; +} + +static void +gtk_cell_renderer_progress_get_preferred_height (GtkCellRenderer *cell, + GtkWidget *widget, + int *minimum, + int *natural) +{ + GtkCellRendererProgress *self = GTK_CELL_RENDERER_PROGRESS (cell); + GtkCellRendererProgressPrivate *priv = gtk_cell_renderer_progress_get_instance_private (self); + int w, h; + int size; + + if (priv->min_w < 0) + { + char *text = g_strdup_printf (C_("progress bar label", "%d %%"), 100); + compute_dimensions (cell, widget, text, + &priv->min_w, + &priv->min_h); + g_free (text); + } + + compute_dimensions (cell, widget, priv->label, &w, &h); + + size = MIN (priv->min_h, h); + + if (minimum != NULL) + *minimum = size; + if (natural != NULL) + *natural = size; +} + +static inline int +get_bar_size (int pulse, + int value, + int full_size) +{ + int bar_size; + + if (pulse < 0) + bar_size = full_size * MAX (0, value) / 100; + else if (pulse == 0) + bar_size = 0; + else if (pulse == G_MAXINT) + bar_size = full_size; + else + bar_size = MAX (2, full_size / 5); + + return bar_size; +} + +static inline int +get_bar_position (int start, + int full_size, + int bar_size, + int pulse, + int offset, + gboolean is_rtl) +{ + int position; + + if (pulse < 0 || pulse == 0 || pulse == G_MAXINT) + { + position = is_rtl ? (start + full_size - bar_size) : start; + } + else + { + position = (is_rtl ? offset + 12 : offset) % 24; + if (position > 12) + position = 24 - position; + position = start + full_size * position / 15; + } + + return position; +} + +static void +gtk_cell_renderer_progress_snapshot (GtkCellRenderer *cell, + GtkSnapshot *snapshot, + GtkWidget *widget, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + GtkCellRendererState flags) +{ + GtkCellRendererProgress *cellprogress = GTK_CELL_RENDERER_PROGRESS (cell); + GtkCellRendererProgressPrivate *priv = gtk_cell_renderer_progress_get_instance_private (cellprogress); + GtkStyleContext *context; + GtkBorder padding; + PangoLayout *layout; + PangoRectangle logical_rect; + int x, y, w, h, x_pos, y_pos, bar_position, bar_size, start, full_size; + int xpad, ypad; + GdkRectangle clip; + gboolean is_rtl; + + context = gtk_widget_get_style_context (widget); + is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL; + + gtk_cell_renderer_get_padding (cell, &xpad, &ypad); + x = cell_area->x + xpad; + y = cell_area->y + ypad; + w = cell_area->width - xpad * 2; + h = cell_area->height - ypad * 2; + + gtk_style_context_save (context); + gtk_style_context_add_class (context, "trough"); + + gtk_snapshot_render_background (snapshot, context, x, y, w, h); + gtk_snapshot_render_frame (snapshot, context, x, y, w, h); + + gtk_style_context_get_padding (context, &padding); + + x += padding.left; + y += padding.top; + w -= padding.left + padding.right; + h -= padding.top + padding.bottom; + + gtk_style_context_restore (context); + + if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) + { + clip.y = y; + clip.height = h; + + start = x; + full_size = w; + + bar_size = get_bar_size (priv->pulse, priv->value, full_size); + + if (!priv->inverted) + bar_position = get_bar_position (start, full_size, bar_size, + priv->pulse, priv->offset, is_rtl); + else + bar_position = get_bar_position (start, full_size, bar_size, + priv->pulse, priv->offset, !is_rtl); + + clip.width = bar_size; + clip.x = bar_position; + } + else + { + clip.x = x; + clip.width = w; + + start = y; + full_size = h; + + bar_size = get_bar_size (priv->pulse, priv->value, full_size); + + if (priv->inverted) + bar_position = get_bar_position (start, full_size, bar_size, + priv->pulse, priv->offset, TRUE); + else + bar_position = get_bar_position (start, full_size, bar_size, + priv->pulse, priv->offset, FALSE); + + clip.height = bar_size; + clip.y = bar_position; + } + + if (bar_size > 0) + { + gtk_style_context_save (context); + gtk_style_context_add_class (context, "progressbar"); + + gtk_snapshot_render_background (snapshot, context, clip.x, clip.y, clip.width, clip.height); + gtk_snapshot_render_frame (snapshot, context, clip.x, clip.y, clip.width, clip.height); + + gtk_style_context_restore (context); + } + + if (priv->label) + { + float text_xalign; + + layout = gtk_widget_create_pango_layout (widget, priv->label); + pango_layout_get_pixel_extents (layout, NULL, &logical_rect); + + if (gtk_widget_get_direction (widget) != GTK_TEXT_DIR_LTR) + text_xalign = 1.0 - priv->text_xalign; + else + text_xalign = priv->text_xalign; + + x_pos = x + padding.left + text_xalign * + (w - padding.left - padding.right - logical_rect.width); + + y_pos = y + padding.top + priv->text_yalign * + (h - padding.top - padding.bottom - logical_rect.height); + + gtk_snapshot_push_clip (snapshot, + &GRAPHENE_RECT_INIT( + clip.x, clip.y, + clip.width, clip.height + )); + + gtk_style_context_save (context); + gtk_style_context_add_class (context, "progressbar"); + + gtk_snapshot_render_layout (snapshot, context, + x_pos, y_pos, + layout); + + gtk_style_context_restore (context); + gtk_snapshot_pop (snapshot); + + gtk_style_context_save (context); + gtk_style_context_add_class (context, "trough"); + + if (bar_position > start) + { + if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) + { + clip.x = x; + clip.width = bar_position - x; + } + else + { + clip.y = y; + clip.height = bar_position - y; + } + + gtk_snapshot_push_clip (snapshot, + &GRAPHENE_RECT_INIT( + clip.x, clip.y, + clip.width, clip.height + )); + + gtk_snapshot_render_layout (snapshot, context, + x_pos, y_pos, + layout); + + gtk_snapshot_pop (snapshot); + } + + if (bar_position + bar_size < start + full_size) + { + if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) + { + clip.x = bar_position + bar_size; + clip.width = x + w - (bar_position + bar_size); + } + else + { + clip.y = bar_position + bar_size; + clip.height = y + h - (bar_position + bar_size); + } + + gtk_snapshot_push_clip (snapshot, + &GRAPHENE_RECT_INIT( + clip.x, clip.y, + clip.width, clip.height + )); + + gtk_snapshot_render_layout (snapshot, context, + x_pos, y_pos, + layout); + + gtk_snapshot_pop (snapshot); + } + + gtk_style_context_restore (context); + g_object_unref (layout); + } +} + +static void +gtk_cell_renderer_progress_class_init (GtkCellRendererProgressClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass); + + object_class->finalize = gtk_cell_renderer_progress_finalize; + object_class->get_property = gtk_cell_renderer_progress_get_property; + object_class->set_property = gtk_cell_renderer_progress_set_property; + + cell_class->get_preferred_width = gtk_cell_renderer_progress_get_preferred_width; + cell_class->get_preferred_height = gtk_cell_renderer_progress_get_preferred_height; + cell_class->snapshot = gtk_cell_renderer_progress_snapshot; + + /** + * GtkCellRendererProgress:value: + * + * The "value" property determines the percentage to which the + * progress bar will be "filled in". + **/ + g_object_class_install_property (object_class, + PROP_VALUE, + g_param_spec_int ("value", NULL, NULL, + 0, 100, 0, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + /** + * GtkCellRendererProgress:text: + * + * The "text" property determines the label which will be drawn + * over the progress bar. Setting this property to %NULL causes the default + * label to be displayed. Setting this property to an empty string causes + * no label to be displayed. + **/ + g_object_class_install_property (object_class, + PROP_TEXT, + g_param_spec_string ("text", NULL, NULL, + NULL, + GTK_PARAM_READWRITE)); + + /** + * GtkCellRendererProgress:pulse: + * + * Setting this to a non-negative value causes the cell renderer to + * enter "activity mode", where a block bounces back and forth to + * indicate that some progress is made, without specifying exactly how + * much. + * + * Each increment of the property causes the block to move by a little + * bit. + * + * To indicate that the activity has not started yet, set the property + * to zero. To indicate completion, set the property to %G_MAXINT. + */ + g_object_class_install_property (object_class, + PROP_PULSE, + g_param_spec_int ("pulse", NULL, NULL, + -1, G_MAXINT, -1, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + /** + * GtkCellRendererProgress:text-xalign: + * + * The "text-xalign" property controls the horizontal alignment of the + * text in the progress bar. Valid values range from 0 (left) to 1 + * (right). Reserved for RTL layouts. + */ + g_object_class_install_property (object_class, + PROP_TEXT_XALIGN, + g_param_spec_float ("text-xalign", NULL, NULL, + 0.0, 1.0, 0.5, + GTK_PARAM_READWRITE)); + + /** + * GtkCellRendererProgress:text-yalign: + * + * The "text-yalign" property controls the vertical alignment of the + * text in the progress bar. Valid values range from 0 (top) to 1 + * (bottom). + */ + g_object_class_install_property (object_class, + PROP_TEXT_YALIGN, + g_param_spec_float ("text-yalign", NULL, NULL, + 0.0, 1.0, 0.5, + GTK_PARAM_READWRITE)); + + g_object_class_override_property (object_class, + PROP_ORIENTATION, + "orientation"); + + g_object_class_install_property (object_class, + PROP_INVERTED, + g_param_spec_boolean ("inverted", NULL, NULL, + FALSE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); +} + +static void +gtk_cell_renderer_progress_init (GtkCellRendererProgress *cellprogress) +{ + GtkCellRendererProgressPrivate *priv = gtk_cell_renderer_progress_get_instance_private (cellprogress); + + priv->value = 0; + priv->text = NULL; + priv->label = NULL; + priv->min_w = -1; + priv->min_h = -1; + priv->pulse = -1; + priv->offset = 0; + + priv->text_xalign = 0.5; + priv->text_yalign = 0.5; + + priv->orientation = GTK_ORIENTATION_HORIZONTAL, + priv->inverted = FALSE; +} + +/** + * gtk_cell_renderer_progress_new: + * + * Creates a new `GtkCellRendererProgress`. + * + * Returns: the new cell renderer + * + * Deprecated: 4.10 + **/ +GtkCellRenderer* +gtk_cell_renderer_progress_new (void) +{ + return g_object_new (GTK_TYPE_CELL_RENDERER_PROGRESS, NULL); +} diff --git a/gtk/deprecated/gtkcellrendererprogress.h b/gtk/deprecated/gtkcellrendererprogress.h new file mode 100644 index 0000000000..1015eb04b0 --- /dev/null +++ b/gtk/deprecated/gtkcellrendererprogress.h @@ -0,0 +1,52 @@ +/* gtkcellrendererprogress.h + * Copyright (C) 2002 Naba Kumar + * modified by Jörgen Scheibengruber + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +/* + * Modified by the GTK+ Team and others 1997-2004. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#ifndef __GTK_CELL_RENDERER_PROGRESS_H__ +#define __GTK_CELL_RENDERER_PROGRESS_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_CELL_RENDERER_PROGRESS (gtk_cell_renderer_progress_get_type ()) +#define GTK_CELL_RENDERER_PROGRESS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_RENDERER_PROGRESS, GtkCellRendererProgress)) +#define GTK_IS_CELL_RENDERER_PROGRESS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_RENDERER_PROGRESS)) + +typedef struct _GtkCellRendererProgress GtkCellRendererProgress; + +GDK_AVAILABLE_IN_ALL +GType gtk_cell_renderer_progress_get_type (void) G_GNUC_CONST; +GDK_DEPRECATED_IN_4_10 +GtkCellRenderer* gtk_cell_renderer_progress_new (void); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkCellRendererProgress, g_object_unref) + +G_END_DECLS + +#endif /* __GTK_CELL_RENDERER_PROGRESS_H__ */ diff --git a/gtk/deprecated/gtkcellrendererspin.c b/gtk/deprecated/gtkcellrendererspin.c new file mode 100644 index 0000000000..60c878726e --- /dev/null +++ b/gtk/deprecated/gtkcellrendererspin.c @@ -0,0 +1,379 @@ +/* GtkCellRendererSpin + * Copyright (C) 2004 Lorenzo Gil Sanchez + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Authors: Lorenzo Gil Sanchez + * Carlos Garnacho Parro + */ + +#include "config.h" + +#include "gtkcellrendererspin.h" + +#include "gtkadjustment.h" +#include "gtkprivate.h" +#include "gtkspinbutton.h" +#include "gtkentry.h" +#include "gtkeventcontrollerkey.h" + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + +/** + * GtkCellRendererSpin: + * + * Renders a spin button in a cell + * + * `GtkCellRendererSpin` renders text in a cell like `GtkCellRendererText` from + * which it is derived. But while `GtkCellRendererText` offers a simple entry to + * edit the text, `GtkCellRendererSpin` offers a `GtkSpinButton` widget. Of course, + * that means that the text has to be parseable as a floating point number. + * + * The range of the spinbutton is taken from the adjustment property of the + * cell renderer, which can be set explicitly or mapped to a column in the + * tree model, like all properties of cell renders. `GtkCellRendererSpin` + * also has properties for the `GtkCellRendererSpin:climb-rate` and the number + * of `GtkCellRendererSpin:digits` to display. Other `GtkSpinButton` properties + * can be set in a handler for the `GtkCellRenderer::editing-started` signal. + * + * The `GtkCellRendererSpin` cell renderer was added in GTK 2.10. + */ + +typedef struct _GtkCellRendererSpinClass GtkCellRendererSpinClass; +typedef struct _GtkCellRendererSpinPrivate GtkCellRendererSpinPrivate; + +struct _GtkCellRendererSpin +{ + GtkCellRendererText parent; +}; + +struct _GtkCellRendererSpinClass +{ + GtkCellRendererTextClass parent; +}; + +struct _GtkCellRendererSpinPrivate +{ + GtkWidget *spin; + GtkAdjustment *adjustment; + double climb_rate; + guint digits; +}; + +static void gtk_cell_renderer_spin_finalize (GObject *object); + +static void gtk_cell_renderer_spin_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *spec); +static void gtk_cell_renderer_spin_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *spec); + +static gboolean gtk_cell_renderer_spin_key_pressed (GtkEventControllerKey *controller, + guint keyval, + guint keycode, + GdkModifierType state, + GtkWidget *widget); + +static GtkCellEditable * gtk_cell_renderer_spin_start_editing (GtkCellRenderer *cell, + GdkEvent *event, + GtkWidget *widget, + const char *path, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + GtkCellRendererState flags); +enum { + PROP_0, + PROP_ADJUSTMENT, + PROP_CLIMB_RATE, + PROP_DIGITS +}; + +#define GTK_CELL_RENDERER_SPIN_PATH "gtk-cell-renderer-spin-path" + +G_DEFINE_TYPE_WITH_PRIVATE (GtkCellRendererSpin, gtk_cell_renderer_spin, GTK_TYPE_CELL_RENDERER_TEXT) + + +static void +gtk_cell_renderer_spin_class_init (GtkCellRendererSpinClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass); + + object_class->finalize = gtk_cell_renderer_spin_finalize; + object_class->get_property = gtk_cell_renderer_spin_get_property; + object_class->set_property = gtk_cell_renderer_spin_set_property; + + cell_class->start_editing = gtk_cell_renderer_spin_start_editing; + + /** + * GtkCellRendererSpin:adjustment: + * + * The adjustment that holds the value of the spinbutton. + * This must be non-%NULL for the cell renderer to be editable. + */ + g_object_class_install_property (object_class, + PROP_ADJUSTMENT, + g_param_spec_object ("adjustment", NULL, NULL, + GTK_TYPE_ADJUSTMENT, + GTK_PARAM_READWRITE)); + + + /** + * GtkCellRendererSpin:climb-rate: + * + * The acceleration rate when you hold down a button. + */ + g_object_class_install_property (object_class, + PROP_CLIMB_RATE, + g_param_spec_double ("climb-rate", NULL, NULL, + 0.0, G_MAXDOUBLE, 0.0, + GTK_PARAM_READWRITE)); + /** + * GtkCellRendererSpin:digits: + * + * The number of decimal places to display. + */ + g_object_class_install_property (object_class, + PROP_DIGITS, + g_param_spec_uint ("digits", NULL, NULL, + 0, 20, 0, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); +} + +static void +gtk_cell_renderer_spin_init (GtkCellRendererSpin *self) +{ + GtkCellRendererSpinPrivate *priv = gtk_cell_renderer_spin_get_instance_private (self); + + priv->adjustment = NULL; + priv->climb_rate = 0.0; + priv->digits = 0; +} + +static void +gtk_cell_renderer_spin_finalize (GObject *object) +{ + GtkCellRendererSpinPrivate *priv = gtk_cell_renderer_spin_get_instance_private (GTK_CELL_RENDERER_SPIN (object)); + + g_clear_object (&priv->adjustment); + g_clear_object (&priv->spin); + + G_OBJECT_CLASS (gtk_cell_renderer_spin_parent_class)->finalize (object); +} + +static void +gtk_cell_renderer_spin_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkCellRendererSpinPrivate *priv = gtk_cell_renderer_spin_get_instance_private (GTK_CELL_RENDERER_SPIN (object)); + + switch (prop_id) + { + case PROP_ADJUSTMENT: + g_value_set_object (value, priv->adjustment); + break; + case PROP_CLIMB_RATE: + g_value_set_double (value, priv->climb_rate); + break; + case PROP_DIGITS: + g_value_set_uint (value, priv->digits); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_cell_renderer_spin_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkCellRendererSpinPrivate *priv = gtk_cell_renderer_spin_get_instance_private (GTK_CELL_RENDERER_SPIN (object)); + GObject *obj; + + switch (prop_id) + { + case PROP_ADJUSTMENT: + obj = g_value_get_object (value); + + if (priv->adjustment) + { + g_object_unref (priv->adjustment); + priv->adjustment = NULL; + } + + if (obj) + priv->adjustment = GTK_ADJUSTMENT (g_object_ref_sink (obj)); + + break; + case PROP_CLIMB_RATE: + priv->climb_rate = g_value_get_double (value); + break; + case PROP_DIGITS: + if (priv->digits != g_value_get_uint (value)) + { + priv->digits = g_value_get_uint (value); + g_object_notify_by_pspec (object, pspec); + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_cell_renderer_spin_focus_changed (GtkWidget *widget, + GParamSpec *pspec, + gpointer data) +{ + const char *path; + const char *new_text; + gboolean canceled; + + if (gtk_widget_has_focus (widget)) + return; + + g_object_get (widget, "editing-canceled", &canceled, NULL); + + g_signal_handlers_disconnect_by_func (widget, + gtk_cell_renderer_spin_focus_changed, + data); + + gtk_cell_renderer_stop_editing (GTK_CELL_RENDERER (data), canceled); + + if (canceled) + return; + + path = g_object_get_data (G_OBJECT (widget), GTK_CELL_RENDERER_SPIN_PATH); + new_text = gtk_editable_get_text (GTK_EDITABLE (widget)); + g_signal_emit_by_name (data, "edited", path, new_text); +} + +static gboolean +gtk_cell_renderer_spin_key_pressed (GtkEventControllerKey *controller, + guint keyval, + guint keycode, + GdkModifierType state, + GtkWidget *widget) +{ + if (state == 0) + { + if (keyval == GDK_KEY_Up) + { + gtk_spin_button_spin (GTK_SPIN_BUTTON (widget), GTK_SPIN_STEP_FORWARD, 1); + return TRUE; + } + else if (keyval == GDK_KEY_Down) + { + gtk_spin_button_spin (GTK_SPIN_BUTTON (widget), GTK_SPIN_STEP_BACKWARD, 1); + return TRUE; + } + } + + return FALSE; +} + +static void +gtk_cell_renderer_spin_editing_done (GtkSpinButton *spin, + GtkCellRendererSpin *cell) +{ + GtkCellRendererSpinPrivate *priv = gtk_cell_renderer_spin_get_instance_private (GTK_CELL_RENDERER_SPIN (cell)); + gboolean canceled; + const char *path; + const char *new_text; + + g_clear_object (&priv->spin); + + g_object_get (spin, "editing-canceled", &canceled, NULL); + gtk_cell_renderer_stop_editing (GTK_CELL_RENDERER (cell), canceled); + + if (canceled) + return; + + path = g_object_get_data (G_OBJECT (spin), GTK_CELL_RENDERER_SPIN_PATH); + new_text = gtk_editable_get_text (GTK_EDITABLE (spin)); + g_signal_emit_by_name (cell, "edited", path, new_text); +} + +static GtkCellEditable * +gtk_cell_renderer_spin_start_editing (GtkCellRenderer *cell, + GdkEvent *event, + GtkWidget *widget, + const char *path, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + GtkCellRendererState flags) +{ + GtkCellRendererSpinPrivate *priv = gtk_cell_renderer_spin_get_instance_private (GTK_CELL_RENDERER_SPIN (cell)); + GtkCellRendererText *cell_text = GTK_CELL_RENDERER_TEXT (cell); + GtkEventController *key_controller; + gboolean editable; + char *text; + + g_object_get (cell_text, "editable", &editable, NULL); + if (!editable) + return NULL; + + if (!priv->adjustment) + return NULL; + + priv->spin = gtk_spin_button_new (priv->adjustment, priv->climb_rate, priv->digits); + g_object_ref_sink (priv->spin); + + g_object_get (cell_text, "text", &text, NULL); + if (text) + { + gtk_spin_button_set_value (GTK_SPIN_BUTTON (priv->spin), g_strtod (text, NULL)); + g_free (text); + } + + key_controller = gtk_event_controller_key_new (); + g_signal_connect (key_controller, "key-pressed", + G_CALLBACK (gtk_cell_renderer_spin_key_pressed), priv->spin); + gtk_widget_add_controller (priv->spin, key_controller); + + g_object_set_data_full (G_OBJECT (priv->spin), GTK_CELL_RENDERER_SPIN_PATH, + g_strdup (path), g_free); + + g_signal_connect (priv->spin, "editing-done", + G_CALLBACK (gtk_cell_renderer_spin_editing_done), cell); + + g_signal_connect (priv->spin, "notify::has-focus", + G_CALLBACK (gtk_cell_renderer_spin_focus_changed), cell); + + return GTK_CELL_EDITABLE (priv->spin); +} + +/** + * gtk_cell_renderer_spin_new: + * + * Creates a new `GtkCellRendererSpin`. + * + * Returns: a new `GtkCellRendererSpin` + * + * Deprecated: 4.10 + */ +GtkCellRenderer * +gtk_cell_renderer_spin_new (void) +{ + return g_object_new (GTK_TYPE_CELL_RENDERER_SPIN, NULL); +} diff --git a/gtk/deprecated/gtkcellrendererspin.h b/gtk/deprecated/gtkcellrendererspin.h new file mode 100644 index 0000000000..9cfbffe875 --- /dev/null +++ b/gtk/deprecated/gtkcellrendererspin.h @@ -0,0 +1,44 @@ +/* GtkCellRendererSpin + * Copyright (C) 2004 Lorenzo Gil Sanchez + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#ifndef __GTK_CELL_RENDERER_SPIN_H__ +#define __GTK_CELL_RENDERER_SPIN_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_CELL_RENDERER_SPIN (gtk_cell_renderer_spin_get_type ()) +#define GTK_CELL_RENDERER_SPIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_RENDERER_SPIN, GtkCellRendererSpin)) +#define GTK_IS_CELL_RENDERER_SPIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_RENDERER_SPIN)) + +typedef struct _GtkCellRendererSpin GtkCellRendererSpin; + +GDK_AVAILABLE_IN_ALL +GType gtk_cell_renderer_spin_get_type (void); +GDK_DEPRECATED_IN_4_10 +GtkCellRenderer *gtk_cell_renderer_spin_new (void); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkCellRendererSpin, g_object_unref) + +G_END_DECLS + +#endif /* __GTK_CELL_RENDERER_SPIN_H__ */ diff --git a/gtk/deprecated/gtkcellrendererspinner.c b/gtk/deprecated/gtkcellrendererspinner.c new file mode 100644 index 0000000000..c32a09e294 --- /dev/null +++ b/gtk/deprecated/gtkcellrendererspinner.c @@ -0,0 +1,492 @@ +/* GTK - The GIMP Toolkit + * + * Copyright (C) 2009 Matthias Clasen + * Copyright (C) 2008 Richard Hughes + * Copyright (C) 2009 Bastien Nocera + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +/* + * Modified by the GTK+ Team and others 2007. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#include "config.h" + +#include "gtkcellrendererspinner.h" +#include "gtkiconhelperprivate.h" +#include "gtksettings.h" +#include "gtksnapshot.h" +#include "gtktypebuiltins.h" +#include "gtkstylecontextprivate.h" +#include "gtkcssnumbervalueprivate.h" + +#include + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + +/** + * GtkCellRendererSpinner: + * + * Renders a spinning animation in a cell + * + * `GtkCellRendererSpinner` renders a spinning animation in a cell, very + * similar to `GtkSpinner`. It can often be used as an alternative + * to a `GtkCellRendererProgress` for displaying indefinite activity, + * instead of actual progress. + * + * To start the animation in a cell, set the `GtkCellRendererSpinner:active` + * property to %TRUE and increment the `GtkCellRendererSpinner:pulse` property + * at regular intervals. The usual way to set the cell renderer properties + * for each cell is to bind them to columns in your tree model using e.g. + * gtk_tree_view_column_add_attribute(). + */ + + +enum { + PROP_0, + PROP_ACTIVE, + PROP_PULSE, + PROP_SIZE +}; + +typedef struct _GtkCellRendererSpinnerClass GtkCellRendererSpinnerClass; +typedef struct _GtkCellRendererSpinnerPrivate GtkCellRendererSpinnerPrivate; + +struct _GtkCellRendererSpinner +{ + GtkCellRenderer parent; +}; + +struct _GtkCellRendererSpinnerClass +{ + GtkCellRendererClass parent_class; + + /* Padding for future expansion */ + void (*_gtk_reserved1) (void); + void (*_gtk_reserved2) (void); + void (*_gtk_reserved3) (void); + void (*_gtk_reserved4) (void); +}; + +struct _GtkCellRendererSpinnerPrivate +{ + gboolean active; + guint pulse; + GtkIconSize icon_size; + int size; +}; + + +static void gtk_cell_renderer_spinner_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void gtk_cell_renderer_spinner_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); +static void gtk_cell_renderer_spinner_get_size (GtkCellRendererSpinner *self, + GtkWidget *widget, + const GdkRectangle *cell_area, + int *x_offset, + int *y_offset, + int *width, + int *height); +static void gtk_cell_renderer_spinner_snapshot (GtkCellRenderer *cell, + GtkSnapshot *snapshot, + GtkWidget *widget, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + GtkCellRendererState flags); + +G_DEFINE_TYPE_WITH_PRIVATE (GtkCellRendererSpinner, gtk_cell_renderer_spinner, GTK_TYPE_CELL_RENDERER) + +static GtkSizeRequestMode +gtk_cell_renderer_spinner_get_request_mode (GtkCellRenderer *cell) +{ + return GTK_SIZE_REQUEST_CONSTANT_SIZE; +} + +static void +gtk_cell_renderer_spinner_get_preferred_width (GtkCellRenderer *cell, + GtkWidget *widget, + int *minimum, + int *natural) +{ + int size = 0; + + gtk_cell_renderer_spinner_get_size (GTK_CELL_RENDERER_SPINNER (cell), widget, + NULL, + NULL, NULL, &size, NULL); + + if (minimum != NULL) + *minimum = size; + if (natural != NULL) + *natural = size; +} + +static void +gtk_cell_renderer_spinner_get_preferred_height (GtkCellRenderer *cell, + GtkWidget *widget, + int *minimum, + int *natural) +{ + int size = 0; + + gtk_cell_renderer_spinner_get_size (GTK_CELL_RENDERER_SPINNER (cell), widget, + NULL, + NULL, NULL, NULL, &size); + + if (minimum != NULL) + *minimum = size; + if (natural != NULL) + *natural = size; +} + +static void +gtk_cell_renderer_spinner_class_init (GtkCellRendererSpinnerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass); + + object_class->get_property = gtk_cell_renderer_spinner_get_property; + object_class->set_property = gtk_cell_renderer_spinner_set_property; + + cell_class->get_request_mode = gtk_cell_renderer_spinner_get_request_mode; + cell_class->get_preferred_width = gtk_cell_renderer_spinner_get_preferred_width; + cell_class->get_preferred_height = gtk_cell_renderer_spinner_get_preferred_height; + cell_class->snapshot = gtk_cell_renderer_spinner_snapshot; + + /* GtkCellRendererSpinner:active: + * + * Whether the spinner is active (ie. shown) in the cell + */ + g_object_class_install_property (object_class, + PROP_ACTIVE, + g_param_spec_boolean ("active", NULL, NULL, + FALSE, + G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + /** + * GtkCellRendererSpinner:pulse: + * + * Pulse of the spinner. Increment this value to draw the next frame of the + * spinner animation. Usually, you would update this value in a timeout. + * + * By default, the `GtkSpinner` widget draws one full cycle of the animation, + * consisting of 12 frames, in 750 milliseconds. + */ + g_object_class_install_property (object_class, + PROP_PULSE, + g_param_spec_uint ("pulse", NULL, NULL, + 0, G_MAXUINT, 0, + G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + /** + * GtkCellRendererSpinner:size: + * + * The `GtkIconSize` value that specifies the size of the rendered spinner. + */ + g_object_class_install_property (object_class, + PROP_SIZE, + g_param_spec_enum ("size", NULL, NULL, + GTK_TYPE_ICON_SIZE, GTK_ICON_SIZE_INHERIT, + G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + +} + +static void +gtk_cell_renderer_spinner_init (GtkCellRendererSpinner *cell) +{ + GtkCellRendererSpinnerPrivate *priv = gtk_cell_renderer_spinner_get_instance_private (cell); + priv->pulse = 0; + priv->icon_size = GTK_ICON_SIZE_INHERIT; +} + +/** + * gtk_cell_renderer_spinner_new: + * + * Returns a new cell renderer which will show a spinner to indicate + * activity. + * + * Returns: a new `GtkCellRenderer` + * + * Deprecated: 4.10 + */ +GtkCellRenderer * +gtk_cell_renderer_spinner_new (void) +{ + return g_object_new (GTK_TYPE_CELL_RENDERER_SPINNER, NULL); +} + +static void +gtk_cell_renderer_spinner_update_size (GtkCellRendererSpinner *cell, + GtkWidget *widget) +{ + GtkCellRendererSpinnerPrivate *priv = gtk_cell_renderer_spinner_get_instance_private (cell); + GtkStyleContext *context; + GtkCssNode *node; + GtkCssStyle *style; + + context = gtk_widget_get_style_context (widget); + gtk_style_context_save (context); + + gtk_style_context_add_class (context, "spinner"); + node = gtk_style_context_get_node (context); + gtk_icon_size_set_style_classes (node, priv->icon_size); + style = gtk_css_node_get_style (node); + priv->size = _gtk_css_number_value_get (style->icon->icon_size, 100); + + gtk_style_context_restore (context); +} + +static void +gtk_cell_renderer_spinner_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + GtkCellRendererSpinner *cell = GTK_CELL_RENDERER_SPINNER (object); + GtkCellRendererSpinnerPrivate *priv = gtk_cell_renderer_spinner_get_instance_private (cell); + + switch (param_id) + { + case PROP_ACTIVE: + g_value_set_boolean (value, priv->active); + break; + case PROP_PULSE: + g_value_set_uint (value, priv->pulse); + break; + case PROP_SIZE: + g_value_set_enum (value, priv->icon_size); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + } +} + +static void +gtk_cell_renderer_spinner_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkCellRendererSpinner *cell = GTK_CELL_RENDERER_SPINNER (object); + GtkCellRendererSpinnerPrivate *priv = gtk_cell_renderer_spinner_get_instance_private (cell); + + switch (param_id) + { + case PROP_ACTIVE: + if (priv->active != g_value_get_boolean (value)) + { + priv->active = g_value_get_boolean (value); + g_object_notify (object, "active"); + } + break; + case PROP_PULSE: + if (priv->pulse != g_value_get_uint (value)) + { + priv->pulse = g_value_get_uint (value); + g_object_notify (object, "pulse"); + } + break; + case PROP_SIZE: + if (priv->icon_size != g_value_get_enum (value)) + { + priv->icon_size = g_value_get_enum (value); + g_object_notify (object, "size"); + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + } +} + +static void +gtk_cell_renderer_spinner_get_size (GtkCellRendererSpinner *self, + GtkWidget *widget, + const GdkRectangle *cell_area, + int *x_offset, + int *y_offset, + int *width, + int *height) +{ + GtkCellRendererSpinnerPrivate *priv = gtk_cell_renderer_spinner_get_instance_private (self); + double align; + int w, h; + int xpad, ypad; + float xalign, yalign; + gboolean rtl; + + rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL; + + gtk_cell_renderer_spinner_update_size (self, widget); + + g_object_get (self, + "xpad", &xpad, + "ypad", &ypad, + "xalign", &xalign, + "yalign", &yalign, + NULL); + + w = h = priv->size; + + if (cell_area) + { + if (x_offset) + { + align = rtl ? 1.0 - xalign : xalign; + *x_offset = align * (cell_area->width - w); + *x_offset = MAX (*x_offset, 0); + } + if (y_offset) + { + align = rtl ? 1.0 - yalign : yalign; + *y_offset = align * (cell_area->height - h); + *y_offset = MAX (*y_offset, 0); + } + } + else + { + if (x_offset) + *x_offset = 0; + if (y_offset) + *y_offset = 0; + } + + if (width) + *width = w; + if (height) + *height = h; +} + +static void +gtk_paint_spinner (GtkStyleContext *context, + cairo_t *cr, + guint step, + int x, + int y, + int width, + int height) +{ + GdkRGBA color; + guint num_steps; + double dx, dy; + double radius; + double half; + int i; + guint real_step; + + num_steps = 12; + real_step = step % num_steps; + + /* set a clip region for the expose event */ + cairo_rectangle (cr, x, y, width, height); + cairo_clip (cr); + + cairo_translate (cr, x, y); + + /* draw clip region */ + cairo_set_operator (cr, CAIRO_OPERATOR_OVER); + + gtk_style_context_get_color (context, &color); + dx = width / 2; + dy = height / 2; + radius = MIN (width / 2, height / 2); + half = num_steps / 2; + + for (i = 0; i < num_steps; i++) + { + int inset = 0.7 * radius; + + /* transparency is a function of time and initial value */ + double t = (double) ((i + num_steps - real_step) + % num_steps) / num_steps; + + cairo_save (cr); + + cairo_set_source_rgba (cr, + color.red / 65535., + color.green / 65535., + color.blue / 65535., + color.alpha * t); + + cairo_set_line_width (cr, 2.0); + cairo_move_to (cr, + dx + (radius - inset) * cos (i * G_PI / half), + dy + (radius - inset) * sin (i * G_PI / half)); + cairo_line_to (cr, + dx + radius * cos (i * G_PI / half), + dy + radius * sin (i * G_PI / half)); + cairo_stroke (cr); + + cairo_restore (cr); + } +} + +static void +gtk_cell_renderer_spinner_snapshot (GtkCellRenderer *cell, + GtkSnapshot *snapshot, + GtkWidget *widget, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + GtkCellRendererState flags) +{ + GtkCellRendererSpinner *self = GTK_CELL_RENDERER_SPINNER (cell); + GtkCellRendererSpinnerPrivate *priv = gtk_cell_renderer_spinner_get_instance_private (self); + GdkRectangle pix_rect; + GdkRectangle draw_rect; + int xpad, ypad; + cairo_t *cr; + + if (!priv->active) + return; + + gtk_cell_renderer_spinner_get_size (self, widget, cell_area, + &pix_rect.x, + &pix_rect.y, + &pix_rect.width, + &pix_rect.height); + + g_object_get (self, + "xpad", &xpad, + "ypad", &ypad, + NULL); + + pix_rect.x += cell_area->x + xpad; + pix_rect.y += cell_area->y + ypad; + pix_rect.width -= xpad * 2; + pix_rect.height -= ypad * 2; + + if (!gdk_rectangle_intersect (cell_area, &pix_rect, &draw_rect)) + return; + + cr = gtk_snapshot_append_cairo (snapshot, + &GRAPHENE_RECT_INIT ( + cell_area->x, cell_area->y, + cell_area->width, cell_area->height + )); + + gtk_paint_spinner (gtk_widget_get_style_context (widget), + cr, + priv->pulse, + draw_rect.x, draw_rect.y, + draw_rect.width, draw_rect.height); + + cairo_destroy (cr); +} diff --git a/gtk/deprecated/gtkcellrendererspinner.h b/gtk/deprecated/gtkcellrendererspinner.h new file mode 100644 index 0000000000..7d4600a00b --- /dev/null +++ b/gtk/deprecated/gtkcellrendererspinner.h @@ -0,0 +1,47 @@ +/* GTK - The GIMP Toolkit + * + * Copyright (C) 2009 Matthias Clasen + * Copyright (C) 2008 Richard Hughes + * Copyright (C) 2009 Bastien Nocera + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#ifndef __GTK_CELL_RENDERER_SPINNER_H__ +#define __GTK_CELL_RENDERER_SPINNER_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_CELL_RENDERER_SPINNER (gtk_cell_renderer_spinner_get_type ()) +#define GTK_CELL_RENDERER_SPINNER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_RENDERER_SPINNER, GtkCellRendererSpinner)) +#define GTK_IS_CELL_RENDERER_SPINNER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_RENDERER_SPINNER)) + +typedef struct _GtkCellRendererSpinner GtkCellRendererSpinner; + +GDK_AVAILABLE_IN_ALL +GType gtk_cell_renderer_spinner_get_type (void) G_GNUC_CONST; +GDK_DEPRECATED_IN_4_10 +GtkCellRenderer *gtk_cell_renderer_spinner_new (void); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkCellRendererSpinner, g_object_unref) + +G_END_DECLS + +#endif /* __GTK_CELL_RENDERER_SPINNER_H__ */ diff --git a/gtk/deprecated/gtkcellrenderertext.c b/gtk/deprecated/gtkcellrenderertext.c new file mode 100644 index 0000000000..579c450c1d --- /dev/null +++ b/gtk/deprecated/gtkcellrenderertext.c @@ -0,0 +1,1941 @@ +/* gtkcellrenderertext.c + * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#include "config.h" + +#include "gtkcellrenderertext.h" + +#include "gtkcssnumbervalueprivate.h" +#include "gtkeditable.h" +#include "gtkentry.h" +#include "gtkentryprivate.h" +#include "gtkmarshalers.h" +#include "gtkprivate.h" +#include "gtksizerequest.h" +#include "gtksnapshot.h" +#include "gtkstylecontextprivate.h" +#include "deprecated/gtktreeprivate.h" + +#include + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + +/** + * GtkCellRendererText: + * + * Renders text in a cell + * + * A `GtkCellRendererText` renders a given text in its cell, using the font, color and + * style information provided by its properties. The text will be ellipsized if it is + * too long and the `GtkCellRendererText:ellipsize` property allows it. + * + * If the `GtkCellRenderer:mode` is %GTK_CELL_RENDERER_MODE_EDITABLE, + * the `GtkCellRendererText` allows to edit its text using an entry. + */ + + +static void gtk_cell_renderer_text_finalize (GObject *object); + +static void gtk_cell_renderer_text_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void gtk_cell_renderer_text_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); +static void gtk_cell_renderer_text_snapshot (GtkCellRenderer *cell, + GtkSnapshot *snapshot, + GtkWidget *widget, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + GtkCellRendererState flags); + +static GtkCellEditable *gtk_cell_renderer_text_start_editing (GtkCellRenderer *cell, + GdkEvent *event, + GtkWidget *widget, + const char *path, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + GtkCellRendererState flags); + +static void gtk_cell_renderer_text_get_preferred_width (GtkCellRenderer *cell, + GtkWidget *widget, + int *minimal_size, + int *natural_size); +static void gtk_cell_renderer_text_get_preferred_height (GtkCellRenderer *cell, + GtkWidget *widget, + int *minimal_size, + int *natural_size); +static void gtk_cell_renderer_text_get_preferred_height_for_width (GtkCellRenderer *cell, + GtkWidget *widget, + int width, + int *minimum_height, + int *natural_height); +static void gtk_cell_renderer_text_get_aligned_area (GtkCellRenderer *cell, + GtkWidget *widget, + GtkCellRendererState flags, + const GdkRectangle *cell_area, + GdkRectangle *aligned_area); + + + +enum { + EDITED, + LAST_SIGNAL +}; + +enum { + PROP_0, + + PROP_TEXT, + PROP_MARKUP, + PROP_ATTRIBUTES, + PROP_SINGLE_PARAGRAPH_MODE, + PROP_WIDTH_CHARS, + PROP_MAX_WIDTH_CHARS, + PROP_WRAP_WIDTH, + PROP_ALIGN, + PROP_PLACEHOLDER_TEXT, + + /* Style args */ + PROP_BACKGROUND, + PROP_FOREGROUND, + PROP_BACKGROUND_RGBA, + PROP_FOREGROUND_RGBA, + PROP_FONT, + PROP_FONT_DESC, + PROP_FAMILY, + PROP_STYLE, + PROP_VARIANT, + PROP_WEIGHT, + PROP_STRETCH, + PROP_SIZE, + PROP_SIZE_POINTS, + PROP_SCALE, + PROP_EDITABLE, + PROP_STRIKETHROUGH, + PROP_UNDERLINE, + PROP_RISE, + PROP_LANGUAGE, + PROP_ELLIPSIZE, + PROP_WRAP_MODE, + + /* Whether-a-style-arg-is-set args */ + PROP_BACKGROUND_SET, + PROP_FOREGROUND_SET, + PROP_FAMILY_SET, + PROP_STYLE_SET, + PROP_VARIANT_SET, + PROP_WEIGHT_SET, + PROP_STRETCH_SET, + PROP_SIZE_SET, + PROP_SCALE_SET, + PROP_EDITABLE_SET, + PROP_STRIKETHROUGH_SET, + PROP_UNDERLINE_SET, + PROP_RISE_SET, + PROP_LANGUAGE_SET, + PROP_ELLIPSIZE_SET, + PROP_ALIGN_SET, + + LAST_PROP +}; + +static guint text_cell_renderer_signals [LAST_SIGNAL]; +static GParamSpec *text_cell_renderer_props [LAST_PROP]; + +#define GTK_CELL_RENDERER_TEXT_PATH "gtk-cell-renderer-text-path" + +typedef struct _GtkCellRendererTextPrivate GtkCellRendererTextPrivate; + +struct _GtkCellRendererTextPrivate +{ + GtkWidget *entry; + + PangoAttrList *extra_attrs; + GdkRGBA foreground; + GdkRGBA background; + PangoAlignment align; + PangoEllipsizeMode ellipsize; + PangoFontDescription *font; + PangoLanguage *language; + PangoUnderline underline_style; + PangoWrapMode wrap_mode; + + char *text; + char *placeholder_text; + + double font_scale; + + int rise; + int fixed_height_rows; + int width_chars; + int max_width_chars; + int wrap_width; + + guint in_entry_menu : 1; + guint strikethrough : 1; + guint editable : 1; + guint scale_set : 1; + guint foreground_set : 1; + guint background_set : 1; + guint underline_set : 1; + guint rise_set : 1; + guint strikethrough_set : 1; + guint editable_set : 1; + guint calc_fixed_height : 1; + guint single_paragraph : 1; + guint language_set : 1; + guint markup_set : 1; + guint ellipsize_set : 1; + guint align_set : 1; + + gulong focus_out_id; + gulong entry_menu_popdown_timeout; +}; + +G_DEFINE_TYPE_WITH_PRIVATE (GtkCellRendererText, gtk_cell_renderer_text, GTK_TYPE_CELL_RENDERER) + +static void +gtk_cell_renderer_text_init (GtkCellRendererText *celltext) +{ + GtkCellRendererTextPrivate *priv = gtk_cell_renderer_text_get_instance_private (celltext); + GtkCellRenderer *cell = GTK_CELL_RENDERER (celltext); + + gtk_cell_renderer_set_alignment (cell, 0.0, 0.5); + gtk_cell_renderer_set_padding (cell, 2, 2); + priv->font_scale = 1.0; + priv->fixed_height_rows = -1; + priv->font = pango_font_description_new (); + + priv->width_chars = -1; + priv->max_width_chars = -1; + priv->wrap_width = -1; + priv->wrap_mode = PANGO_WRAP_CHAR; + priv->align = PANGO_ALIGN_LEFT; + priv->align_set = FALSE; +} + +static void +gtk_cell_renderer_text_class_init (GtkCellRendererTextClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (class); + + object_class->finalize = gtk_cell_renderer_text_finalize; + + object_class->get_property = gtk_cell_renderer_text_get_property; + object_class->set_property = gtk_cell_renderer_text_set_property; + + cell_class->snapshot = gtk_cell_renderer_text_snapshot; + cell_class->start_editing = gtk_cell_renderer_text_start_editing; + cell_class->get_preferred_width = gtk_cell_renderer_text_get_preferred_width; + cell_class->get_preferred_height = gtk_cell_renderer_text_get_preferred_height; + cell_class->get_preferred_height_for_width = gtk_cell_renderer_text_get_preferred_height_for_width; + cell_class->get_aligned_area = gtk_cell_renderer_text_get_aligned_area; + + text_cell_renderer_props[PROP_TEXT] = + g_param_spec_string ("text", NULL, NULL, + NULL, + GTK_PARAM_READWRITE); + + text_cell_renderer_props[PROP_MARKUP] = + g_param_spec_string ("markup", NULL, NULL, + NULL, + GTK_PARAM_WRITABLE); + + text_cell_renderer_props[PROP_ATTRIBUTES] = + g_param_spec_boxed ("attributes", NULL, NULL, + PANGO_TYPE_ATTR_LIST, + GTK_PARAM_READWRITE); + + text_cell_renderer_props[PROP_SINGLE_PARAGRAPH_MODE] = + g_param_spec_boolean ("single-paragraph-mode", NULL, NULL, + FALSE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + + text_cell_renderer_props[PROP_BACKGROUND] = + g_param_spec_string ("background", NULL, NULL, + NULL, + GTK_PARAM_WRITABLE); + + /** + * GtkCellRendererText:background-rgba: + * + * Background color as a `GdkRGBA` + */ + text_cell_renderer_props[PROP_BACKGROUND_RGBA] = + g_param_spec_boxed ("background-rgba", NULL, NULL, + GDK_TYPE_RGBA, + GTK_PARAM_READWRITE); + text_cell_renderer_props[PROP_FOREGROUND] = + g_param_spec_string ("foreground", NULL, NULL, + NULL, + GTK_PARAM_WRITABLE); + + /** + * GtkCellRendererText:foreground-rgba: + * + * Foreground color as a `GdkRGBA` + */ + text_cell_renderer_props[PROP_FOREGROUND_RGBA] = + g_param_spec_boxed ("foreground-rgba", NULL, NULL, + GDK_TYPE_RGBA, + GTK_PARAM_READWRITE); + + + text_cell_renderer_props[PROP_EDITABLE] = + g_param_spec_boolean ("editable", NULL, NULL, + FALSE, + GTK_PARAM_READWRITE); + + text_cell_renderer_props[PROP_FONT] = + g_param_spec_string ("font", NULL, NULL, + NULL, + GTK_PARAM_READWRITE); + + text_cell_renderer_props[PROP_FONT_DESC] = + g_param_spec_boxed ("font-desc", NULL, NULL, + PANGO_TYPE_FONT_DESCRIPTION, + GTK_PARAM_READWRITE); + + text_cell_renderer_props[PROP_FAMILY] = + g_param_spec_string ("family", NULL, NULL, + NULL, + GTK_PARAM_READWRITE); + + text_cell_renderer_props[PROP_STYLE] = + g_param_spec_enum ("style", NULL, NULL, + PANGO_TYPE_STYLE, + PANGO_STYLE_NORMAL, + GTK_PARAM_READWRITE); + + text_cell_renderer_props[PROP_VARIANT] = + g_param_spec_enum ("variant", NULL, NULL, + PANGO_TYPE_VARIANT, + PANGO_VARIANT_NORMAL, + GTK_PARAM_READWRITE); + + text_cell_renderer_props[PROP_WEIGHT] = + g_param_spec_int ("weight", NULL, NULL, + 0, G_MAXINT, + PANGO_WEIGHT_NORMAL, + GTK_PARAM_READWRITE); + + text_cell_renderer_props[PROP_STRETCH] = + g_param_spec_enum ("stretch", NULL, NULL, + PANGO_TYPE_STRETCH, + PANGO_STRETCH_NORMAL, + GTK_PARAM_READWRITE); + + text_cell_renderer_props[PROP_SIZE] = + g_param_spec_int ("size", NULL, NULL, + 0, G_MAXINT, + 0, + GTK_PARAM_READWRITE); + + text_cell_renderer_props[PROP_SIZE_POINTS] = + g_param_spec_double ("size-points", NULL, NULL, + 0.0, G_MAXDOUBLE, + 0.0, + GTK_PARAM_READWRITE); + + text_cell_renderer_props[PROP_SCALE] = + g_param_spec_double ("scale", NULL, NULL, + 0.0, G_MAXDOUBLE, + 1.0, + GTK_PARAM_READWRITE); + + text_cell_renderer_props[PROP_RISE] = + g_param_spec_int ("rise", NULL, NULL, + -G_MAXINT, G_MAXINT, + 0, + GTK_PARAM_READWRITE); + + + text_cell_renderer_props[PROP_STRIKETHROUGH] = + g_param_spec_boolean ("strikethrough", NULL, NULL, + FALSE, + GTK_PARAM_READWRITE); + + text_cell_renderer_props[PROP_UNDERLINE] = + g_param_spec_enum ("underline", NULL, NULL, + PANGO_TYPE_UNDERLINE, + PANGO_UNDERLINE_NONE, + GTK_PARAM_READWRITE); + + text_cell_renderer_props[PROP_LANGUAGE] = + g_param_spec_string ("language", NULL, NULL, + NULL, + GTK_PARAM_READWRITE); + + /** + * GtkCellRendererText:ellipsize: + * + * Specifies the preferred place to ellipsize the string, if the cell renderer + * does not have enough room to display the entire string. Setting it to + * %PANGO_ELLIPSIZE_NONE turns off ellipsizing. See the wrap-width property + * for another way of making the text fit into a given width. + */ + text_cell_renderer_props[PROP_ELLIPSIZE] = + g_param_spec_enum ("ellipsize", NULL, NULL, + PANGO_TYPE_ELLIPSIZE_MODE, + PANGO_ELLIPSIZE_NONE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + + /** + * GtkCellRendererText:width-chars: + * + * The desired width of the cell, in characters. If this property is set to + * -1, the width will be calculated automatically, otherwise the cell will + * request either 3 characters or the property value, whichever is greater. + **/ + text_cell_renderer_props[PROP_WIDTH_CHARS] = + g_param_spec_int ("width-chars", NULL, NULL, + -1, G_MAXINT, + -1, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + + /** + * GtkCellRendererText:max-width-chars: + * + * The desired maximum width of the cell, in characters. If this property + * is set to -1, the width will be calculated automatically. + * + * For cell renderers that ellipsize or wrap text; this property + * controls the maximum reported width of the cell. The + * cell should not receive any greater allocation unless it is + * set to expand in its `GtkCellLayout` and all of the cell's siblings + * have received their natural width. + **/ + text_cell_renderer_props[PROP_MAX_WIDTH_CHARS] = + g_param_spec_int ("max-width-chars", NULL, NULL, + -1, G_MAXINT, + -1, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + + /** + * GtkCellRendererText:wrap-mode: + * + * Specifies how to break the string into multiple lines, if the cell + * renderer does not have enough room to display the entire string. + * This property has no effect unless the wrap-width property is set. + */ + text_cell_renderer_props[PROP_WRAP_MODE] = + g_param_spec_enum ("wrap-mode", NULL, NULL, + PANGO_TYPE_WRAP_MODE, + PANGO_WRAP_CHAR, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + + /** + * GtkCellRendererText:wrap-width: + * + * Specifies the minimum width at which the text is wrapped. The wrap-mode property can + * be used to influence at what character positions the line breaks can be placed. + * Setting wrap-width to -1 turns wrapping off. + */ + text_cell_renderer_props[PROP_WRAP_WIDTH] = + g_param_spec_int ("wrap-width", NULL, NULL, + -1, G_MAXINT, + -1, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + + /** + * GtkCellRendererText:alignment: + * + * Specifies how to align the lines of text with respect to each other. + * + * Note that this property describes how to align the lines of text in + * case there are several of them. The "xalign" property of `GtkCellRenderer`, + * on the other hand, sets the horizontal alignment of the whole text. + */ + text_cell_renderer_props[PROP_ALIGN] = + g_param_spec_enum ("alignment", NULL, NULL, + PANGO_TYPE_ALIGNMENT, + PANGO_ALIGN_LEFT, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + + /** + * GtkCellRendererText:placeholder-text: + * + * The text that will be displayed in the `GtkCellRenderer` if + * `GtkCellRendererText:editable` is %TRUE and the cell is empty. + */ + text_cell_renderer_props[PROP_PLACEHOLDER_TEXT] = + g_param_spec_string ("placeholder-text", NULL, NULL, + NULL, + GTK_PARAM_READWRITE); + + /* Style props are set or not */ + +#define ADD_SET_PROP(propname, propval, nick, blurb) text_cell_renderer_props[propval] = g_param_spec_boolean (propname, nick, blurb, FALSE, GTK_PARAM_READWRITE) + + ADD_SET_PROP ("background-set", PROP_BACKGROUND_SET, NULL, NULL); + + ADD_SET_PROP ("foreground-set", PROP_FOREGROUND_SET, NULL, NULL); + + ADD_SET_PROP ("editable-set", PROP_EDITABLE_SET, NULL, NULL); + + ADD_SET_PROP ("family-set", PROP_FAMILY_SET, NULL, NULL); + + ADD_SET_PROP ("style-set", PROP_STYLE_SET, NULL, NULL); + + ADD_SET_PROP ("variant-set", PROP_VARIANT_SET, NULL, NULL); + + ADD_SET_PROP ("weight-set", PROP_WEIGHT_SET, NULL, NULL); + + ADD_SET_PROP ("stretch-set", PROP_STRETCH_SET, NULL, NULL); + + ADD_SET_PROP ("size-set", PROP_SIZE_SET, NULL, NULL); + + ADD_SET_PROP ("scale-set", PROP_SCALE_SET, NULL, NULL); + + ADD_SET_PROP ("rise-set", PROP_RISE_SET, NULL, NULL); + + ADD_SET_PROP ("strikethrough-set", PROP_STRIKETHROUGH_SET, NULL, NULL); + + ADD_SET_PROP ("underline-set", PROP_UNDERLINE_SET, NULL, NULL); + + ADD_SET_PROP ("language-set", PROP_LANGUAGE_SET, NULL, NULL); + + ADD_SET_PROP ("ellipsize-set", PROP_ELLIPSIZE_SET, NULL, NULL); + + ADD_SET_PROP ("align-set", PROP_ALIGN_SET, NULL, NULL); + + g_object_class_install_properties (object_class, LAST_PROP, text_cell_renderer_props); + + /** + * GtkCellRendererText::edited: + * @renderer: the object which received the signal + * @path: the path identifying the edited cell + * @new_text: the new text + * + * This signal is emitted after @renderer has been edited. + * + * It is the responsibility of the application to update the model + * and store @new_text at the position indicated by @path. + */ + text_cell_renderer_signals [EDITED] = + g_signal_new (I_("edited"), + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkCellRendererTextClass, edited), + NULL, NULL, + _gtk_marshal_VOID__STRING_STRING, + G_TYPE_NONE, 2, + G_TYPE_STRING, + G_TYPE_STRING); + g_signal_set_va_marshaller (text_cell_renderer_signals [EDITED], + G_OBJECT_CLASS_TYPE (object_class), + _gtk_marshal_VOID__STRING_STRINGv); +} + +static void +gtk_cell_renderer_text_finalize (GObject *object) +{ + GtkCellRendererText *celltext = GTK_CELL_RENDERER_TEXT (object); + GtkCellRendererTextPrivate *priv = gtk_cell_renderer_text_get_instance_private (celltext); + + pango_font_description_free (priv->font); + + g_free (priv->text); + g_free (priv->placeholder_text); + + if (priv->extra_attrs) + pango_attr_list_unref (priv->extra_attrs); + + if (priv->language) + g_object_unref (priv->language); + + g_clear_object (&priv->entry); + + G_OBJECT_CLASS (gtk_cell_renderer_text_parent_class)->finalize (object); +} + +static PangoFontMask +get_property_font_set_mask (guint prop_id) +{ + switch (prop_id) + { + case PROP_FAMILY_SET: + return PANGO_FONT_MASK_FAMILY; + case PROP_STYLE_SET: + return PANGO_FONT_MASK_STYLE; + case PROP_VARIANT_SET: + return PANGO_FONT_MASK_VARIANT; + case PROP_WEIGHT_SET: + return PANGO_FONT_MASK_WEIGHT; + case PROP_STRETCH_SET: + return PANGO_FONT_MASK_STRETCH; + case PROP_SIZE_SET: + return PANGO_FONT_MASK_SIZE; + default: + return 0; + } +} + +static void +gtk_cell_renderer_text_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + GtkCellRendererText *celltext = GTK_CELL_RENDERER_TEXT (object); + GtkCellRendererTextPrivate *priv = gtk_cell_renderer_text_get_instance_private (celltext); + + switch (param_id) + { + case PROP_TEXT: + g_value_set_string (value, priv->text); + break; + + case PROP_ATTRIBUTES: + g_value_set_boxed (value, priv->extra_attrs); + break; + + case PROP_SINGLE_PARAGRAPH_MODE: + g_value_set_boolean (value, priv->single_paragraph); + break; + + case PROP_BACKGROUND_RGBA: + g_value_set_boxed (value, &priv->background); + break; + + case PROP_FOREGROUND_RGBA: + g_value_set_boxed (value, &priv->foreground); + break; + + case PROP_FONT: + g_value_take_string (value, pango_font_description_to_string (priv->font)); + break; + + case PROP_FONT_DESC: + g_value_set_boxed (value, priv->font); + break; + + case PROP_FAMILY: + g_value_set_string (value, pango_font_description_get_family (priv->font)); + break; + + case PROP_STYLE: + g_value_set_enum (value, pango_font_description_get_style (priv->font)); + break; + + case PROP_VARIANT: + g_value_set_enum (value, pango_font_description_get_variant (priv->font)); + break; + + case PROP_WEIGHT: + g_value_set_int (value, pango_font_description_get_weight (priv->font)); + break; + + case PROP_STRETCH: + g_value_set_enum (value, pango_font_description_get_stretch (priv->font)); + break; + + case PROP_SIZE: + g_value_set_int (value, pango_font_description_get_size (priv->font)); + break; + + case PROP_SIZE_POINTS: + g_value_set_double (value, ((double)pango_font_description_get_size (priv->font)) / (double)PANGO_SCALE); + break; + + case PROP_SCALE: + g_value_set_double (value, priv->font_scale); + break; + + case PROP_EDITABLE: + g_value_set_boolean (value, priv->editable); + break; + + case PROP_STRIKETHROUGH: + g_value_set_boolean (value, priv->strikethrough); + break; + + case PROP_UNDERLINE: + g_value_set_enum (value, priv->underline_style); + break; + + case PROP_RISE: + g_value_set_int (value, priv->rise); + break; + + case PROP_LANGUAGE: + g_value_set_static_string (value, pango_language_to_string (priv->language)); + break; + + case PROP_ELLIPSIZE: + g_value_set_enum (value, priv->ellipsize); + break; + + case PROP_WRAP_MODE: + g_value_set_enum (value, priv->wrap_mode); + break; + + case PROP_WRAP_WIDTH: + g_value_set_int (value, priv->wrap_width); + break; + + case PROP_ALIGN: + g_value_set_enum (value, priv->align); + break; + + case PROP_BACKGROUND_SET: + g_value_set_boolean (value, priv->background_set); + break; + + case PROP_FOREGROUND_SET: + g_value_set_boolean (value, priv->foreground_set); + break; + + case PROP_FAMILY_SET: + case PROP_STYLE_SET: + case PROP_VARIANT_SET: + case PROP_WEIGHT_SET: + case PROP_STRETCH_SET: + case PROP_SIZE_SET: + { + PangoFontMask mask = get_property_font_set_mask (param_id); + g_value_set_boolean (value, (pango_font_description_get_set_fields (priv->font) & mask) != 0); + + break; + } + + case PROP_SCALE_SET: + g_value_set_boolean (value, priv->scale_set); + break; + + case PROP_EDITABLE_SET: + g_value_set_boolean (value, priv->editable_set); + break; + + case PROP_STRIKETHROUGH_SET: + g_value_set_boolean (value, priv->strikethrough_set); + break; + + case PROP_UNDERLINE_SET: + g_value_set_boolean (value, priv->underline_set); + break; + + case PROP_RISE_SET: + g_value_set_boolean (value, priv->rise_set); + break; + + case PROP_LANGUAGE_SET: + g_value_set_boolean (value, priv->language_set); + break; + + case PROP_ELLIPSIZE_SET: + g_value_set_boolean (value, priv->ellipsize_set); + break; + + case PROP_ALIGN_SET: + g_value_set_boolean (value, priv->align_set); + break; + + case PROP_WIDTH_CHARS: + g_value_set_int (value, priv->width_chars); + break; + + case PROP_MAX_WIDTH_CHARS: + g_value_set_int (value, priv->max_width_chars); + break; + + case PROP_PLACEHOLDER_TEXT: + g_value_set_string (value, priv->placeholder_text); + break; + + case PROP_BACKGROUND: + case PROP_FOREGROUND: + case PROP_MARKUP: + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + + +static void +set_bg_color (GtkCellRendererText *celltext, + GdkRGBA *rgba) +{ + GtkCellRendererTextPrivate *priv = gtk_cell_renderer_text_get_instance_private (celltext); + + if (rgba) + { + if (!priv->background_set) + { + priv->background_set = TRUE; + g_object_notify_by_pspec (G_OBJECT (celltext), text_cell_renderer_props[PROP_BACKGROUND_SET]); + } + + priv->background = *rgba; + } + else + { + if (priv->background_set) + { + priv->background_set = FALSE; + g_object_notify_by_pspec (G_OBJECT (celltext), text_cell_renderer_props[PROP_BACKGROUND_SET]); + } + } +} + +static void +set_fg_color (GtkCellRendererText *celltext, + GdkRGBA *rgba) +{ + GtkCellRendererTextPrivate *priv = gtk_cell_renderer_text_get_instance_private (celltext); + + if (rgba) + { + if (!priv->foreground_set) + { + priv->foreground_set = TRUE; + g_object_notify_by_pspec (G_OBJECT (celltext), text_cell_renderer_props[PROP_FOREGROUND_SET]); + } + + priv->foreground = *rgba; + } + else + { + if (priv->foreground_set) + { + priv->foreground_set = FALSE; + g_object_notify_by_pspec (G_OBJECT (celltext), text_cell_renderer_props[PROP_FOREGROUND_SET]); + } + } +} + +static PangoFontMask +set_font_desc_fields (PangoFontDescription *desc, + PangoFontMask to_set) +{ + PangoFontMask changed_mask = 0; + + if (to_set & PANGO_FONT_MASK_FAMILY) + { + const char *family = pango_font_description_get_family (desc); + if (!family) + { + family = "sans"; + changed_mask |= PANGO_FONT_MASK_FAMILY; + } + + pango_font_description_set_family (desc, family); + } + if (to_set & PANGO_FONT_MASK_STYLE) + pango_font_description_set_style (desc, pango_font_description_get_style (desc)); + if (to_set & PANGO_FONT_MASK_VARIANT) + pango_font_description_set_variant (desc, pango_font_description_get_variant (desc)); + if (to_set & PANGO_FONT_MASK_WEIGHT) + pango_font_description_set_weight (desc, pango_font_description_get_weight (desc)); + if (to_set & PANGO_FONT_MASK_STRETCH) + pango_font_description_set_stretch (desc, pango_font_description_get_stretch (desc)); + if (to_set & PANGO_FONT_MASK_SIZE) + { + int size = pango_font_description_get_size (desc); + if (size <= 0) + { + size = 10 * PANGO_SCALE; + changed_mask |= PANGO_FONT_MASK_SIZE; + } + + pango_font_description_set_size (desc, size); + } + + return changed_mask; +} + +static void +notify_set_changed (GObject *object, + PangoFontMask changed_mask) +{ + if (changed_mask & PANGO_FONT_MASK_FAMILY) + g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_FAMILY_SET]); + if (changed_mask & PANGO_FONT_MASK_STYLE) + g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_STYLE_SET]); + if (changed_mask & PANGO_FONT_MASK_VARIANT) + g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_VARIANT_SET]); + if (changed_mask & PANGO_FONT_MASK_WEIGHT) + g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_WEIGHT_SET]); + if (changed_mask & PANGO_FONT_MASK_STRETCH) + g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_STRETCH_SET]); + if (changed_mask & PANGO_FONT_MASK_SIZE) + g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_SIZE_SET]); +} + +static void +notify_fields_changed (GObject *object, + PangoFontMask changed_mask) +{ + if (changed_mask & PANGO_FONT_MASK_FAMILY) + g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_FAMILY]); + if (changed_mask & PANGO_FONT_MASK_STYLE) + g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_STYLE]); + if (changed_mask & PANGO_FONT_MASK_VARIANT) + g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_VARIANT]); + if (changed_mask & PANGO_FONT_MASK_WEIGHT) + g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_WEIGHT]); + if (changed_mask & PANGO_FONT_MASK_STRETCH) + g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_STRETCH]); + if (changed_mask & PANGO_FONT_MASK_SIZE) + { + g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_SIZE]); + g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_SIZE_POINTS]); + } +} + +static void +set_font_description (GtkCellRendererText *celltext, + PangoFontDescription *font_desc) +{ + GtkCellRendererTextPrivate *priv = gtk_cell_renderer_text_get_instance_private (celltext); + GObject *object = G_OBJECT (celltext); + PangoFontDescription *new_font_desc; + PangoFontMask old_mask, new_mask, changed_mask, set_changed_mask; + + if (font_desc) + new_font_desc = pango_font_description_copy (font_desc); + else + new_font_desc = pango_font_description_new (); + + old_mask = pango_font_description_get_set_fields (priv->font); + new_mask = pango_font_description_get_set_fields (new_font_desc); + + changed_mask = old_mask | new_mask; + set_changed_mask = old_mask ^ new_mask; + + pango_font_description_free (priv->font); + priv->font = new_font_desc; + + g_object_freeze_notify (object); + + g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_FONT_DESC]); + g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_FONT]); + + notify_fields_changed (object, changed_mask); + notify_set_changed (object, set_changed_mask); + + g_object_thaw_notify (object); +} + +static void +gtk_cell_renderer_text_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkCellRendererText *celltext = GTK_CELL_RENDERER_TEXT (object); + GtkCellRendererTextPrivate *priv = gtk_cell_renderer_text_get_instance_private (celltext); + + switch (param_id) + { + case PROP_TEXT: + g_free (priv->text); + + if (priv->markup_set) + { + if (priv->extra_attrs) + pango_attr_list_unref (priv->extra_attrs); + priv->extra_attrs = NULL; + priv->markup_set = FALSE; + } + + priv->text = g_value_dup_string (value); + g_object_notify_by_pspec (object, pspec); + break; + + case PROP_ATTRIBUTES: + if (priv->extra_attrs) + pango_attr_list_unref (priv->extra_attrs); + + priv->extra_attrs = g_value_get_boxed (value); + if (priv->extra_attrs) + pango_attr_list_ref (priv->extra_attrs); + break; + case PROP_MARKUP: + { + const char *str; + char *text = NULL; + GError *error = NULL; + PangoAttrList *attrs = NULL; + + str = g_value_get_string (value); + if (str && !pango_parse_markup (str, -1, 0, &attrs, &text, NULL, &error)) + { + g_warning ("Failed to set text from markup due to error parsing markup: %s", + error->message); + g_error_free (error); + return; + } + + g_free (priv->text); + + if (priv->extra_attrs) + pango_attr_list_unref (priv->extra_attrs); + + priv->text = text; + priv->extra_attrs = attrs; + priv->markup_set = TRUE; + } + break; + + case PROP_SINGLE_PARAGRAPH_MODE: + if (priv->single_paragraph != g_value_get_boolean (value)) + { + priv->single_paragraph = g_value_get_boolean (value); + g_object_notify_by_pspec (object, pspec); + } + break; + + case PROP_BACKGROUND: + { + GdkRGBA rgba; + + if (!g_value_get_string (value)) + set_bg_color (celltext, NULL); /* reset to background_set to FALSE */ + else if (gdk_rgba_parse (&rgba, g_value_get_string (value))) + set_bg_color (celltext, &rgba); + else + g_warning ("Don't know color '%s'", g_value_get_string (value)); + } + break; + + case PROP_FOREGROUND: + { + GdkRGBA rgba; + + if (!g_value_get_string (value)) + set_fg_color (celltext, NULL); /* reset to foreground_set to FALSE */ + else if (gdk_rgba_parse (&rgba, g_value_get_string (value))) + set_fg_color (celltext, &rgba); + else + g_warning ("Don't know color '%s'", g_value_get_string (value)); + } + break; + + case PROP_BACKGROUND_RGBA: + set_bg_color (celltext, g_value_get_boxed (value)); + break; + + case PROP_FOREGROUND_RGBA: + set_fg_color (celltext, g_value_get_boxed (value)); + break; + + case PROP_FONT: + { + PangoFontDescription *font_desc = NULL; + const char *name; + + name = g_value_get_string (value); + + if (name) + font_desc = pango_font_description_from_string (name); + + set_font_description (celltext, font_desc); + + pango_font_description_free (font_desc); + + if (priv->fixed_height_rows != -1) + priv->calc_fixed_height = TRUE; + } + break; + + case PROP_FONT_DESC: + set_font_description (celltext, g_value_get_boxed (value)); + + if (priv->fixed_height_rows != -1) + priv->calc_fixed_height = TRUE; + break; + + case PROP_FAMILY: + case PROP_STYLE: + case PROP_VARIANT: + case PROP_WEIGHT: + case PROP_STRETCH: + case PROP_SIZE: + case PROP_SIZE_POINTS: + { + PangoFontMask old_set_mask = pango_font_description_get_set_fields (priv->font); + + switch (param_id) + { + case PROP_FAMILY: + pango_font_description_set_family (priv->font, + g_value_get_string (value)); + break; + case PROP_STYLE: + pango_font_description_set_style (priv->font, + g_value_get_enum (value)); + break; + case PROP_VARIANT: + pango_font_description_set_variant (priv->font, + g_value_get_enum (value)); + break; + case PROP_WEIGHT: + pango_font_description_set_weight (priv->font, + g_value_get_int (value)); + break; + case PROP_STRETCH: + pango_font_description_set_stretch (priv->font, + g_value_get_enum (value)); + break; + case PROP_SIZE: + pango_font_description_set_size (priv->font, + g_value_get_int (value)); + g_object_notify_by_pspec (object, pspec); + break; + case PROP_SIZE_POINTS: + pango_font_description_set_size (priv->font, + g_value_get_double (value) * PANGO_SCALE); + g_object_notify_by_pspec (object, pspec); + break; + default: + break; + } + + if (priv->fixed_height_rows != -1) + priv->calc_fixed_height = TRUE; + + notify_set_changed (object, old_set_mask & pango_font_description_get_set_fields (priv->font)); + g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_FONT_DESC]); + g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_FONT]); + + break; + } + + case PROP_SCALE: + priv->font_scale = g_value_get_double (value); + priv->scale_set = TRUE; + if (priv->fixed_height_rows != -1) + priv->calc_fixed_height = TRUE; + g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_SCALE_SET]); + break; + + case PROP_EDITABLE: + priv->editable = g_value_get_boolean (value); + priv->editable_set = TRUE; + if (priv->editable) + g_object_set (celltext, "mode", GTK_CELL_RENDERER_MODE_EDITABLE, NULL); + else + g_object_set (celltext, "mode", GTK_CELL_RENDERER_MODE_INERT, NULL); + g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_EDITABLE_SET]); + break; + + case PROP_STRIKETHROUGH: + priv->strikethrough = g_value_get_boolean (value); + priv->strikethrough_set = TRUE; + g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_STRIKETHROUGH_SET]); + break; + + case PROP_UNDERLINE: + priv->underline_style = g_value_get_enum (value); + priv->underline_set = TRUE; + g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_UNDERLINE_SET]); + + break; + + case PROP_RISE: + priv->rise = g_value_get_int (value); + priv->rise_set = TRUE; + g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_RISE_SET]); + if (priv->fixed_height_rows != -1) + priv->calc_fixed_height = TRUE; + break; + + case PROP_LANGUAGE: + priv->language_set = TRUE; + if (priv->language) + g_object_unref (priv->language); + priv->language = pango_language_from_string (g_value_get_string (value)); + g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_LANGUAGE_SET]); + break; + + case PROP_ELLIPSIZE: + priv->ellipsize = g_value_get_enum (value); + priv->ellipsize_set = TRUE; + g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_ELLIPSIZE_SET]); + break; + + case PROP_WRAP_MODE: + if (priv->wrap_mode != g_value_get_enum (value)) + { + priv->wrap_mode = g_value_get_enum (value); + g_object_notify_by_pspec (object, pspec); + } + break; + + case PROP_WRAP_WIDTH: + if (priv->wrap_width != g_value_get_int (value)) + { + priv->wrap_width = g_value_get_int (value); + g_object_notify_by_pspec (object, pspec); + } + break; + + case PROP_WIDTH_CHARS: + if (priv->width_chars != g_value_get_int (value)) + { + priv->width_chars = g_value_get_int (value); + g_object_notify_by_pspec (object, pspec); + } + break; + + case PROP_MAX_WIDTH_CHARS: + if (priv->max_width_chars != g_value_get_int (value)) + { + priv->max_width_chars = g_value_get_int (value); + g_object_notify_by_pspec (object, pspec); + } + break; + + case PROP_ALIGN: + if (priv->align != g_value_get_enum (value)) + { + priv->align = g_value_get_enum (value); + g_object_notify_by_pspec (object, pspec); + } + priv->align_set = TRUE; + g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_ALIGN_SET]); + break; + + case PROP_BACKGROUND_SET: + priv->background_set = g_value_get_boolean (value); + break; + + case PROP_FOREGROUND_SET: + priv->foreground_set = g_value_get_boolean (value); + break; + + case PROP_FAMILY_SET: + case PROP_STYLE_SET: + case PROP_VARIANT_SET: + case PROP_WEIGHT_SET: + case PROP_STRETCH_SET: + case PROP_SIZE_SET: + if (!g_value_get_boolean (value)) + { + pango_font_description_unset_fields (priv->font, + get_property_font_set_mask (param_id)); + } + else + { + PangoFontMask changed_mask; + + changed_mask = set_font_desc_fields (priv->font, + get_property_font_set_mask (param_id)); + notify_fields_changed (G_OBJECT (celltext), changed_mask); + } + break; + + case PROP_SCALE_SET: + priv->scale_set = g_value_get_boolean (value); + break; + + case PROP_EDITABLE_SET: + priv->editable_set = g_value_get_boolean (value); + break; + + case PROP_STRIKETHROUGH_SET: + priv->strikethrough_set = g_value_get_boolean (value); + break; + + case PROP_UNDERLINE_SET: + priv->underline_set = g_value_get_boolean (value); + break; + + case PROP_RISE_SET: + priv->rise_set = g_value_get_boolean (value); + break; + + case PROP_LANGUAGE_SET: + priv->language_set = g_value_get_boolean (value); + break; + + case PROP_ELLIPSIZE_SET: + priv->ellipsize_set = g_value_get_boolean (value); + break; + + case PROP_ALIGN_SET: + priv->align_set = g_value_get_boolean (value); + break; + + case PROP_PLACEHOLDER_TEXT: + g_free (priv->placeholder_text); + priv->placeholder_text = g_value_dup_string (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +/** + * gtk_cell_renderer_text_new: + * + * Creates a new `GtkCellRendererText`. Adjust how text is drawn using + * object properties. Object properties can be + * set globally (with g_object_set()). Also, with `GtkTreeViewColumn`, + * you can bind a property to a value in a `GtkTreeModel`. For example, + * you can bind the “text” property on the cell renderer to a string + * value in the model, thus rendering a different string in each row + * of the `GtkTreeView`. + * + * Returns: the new cell renderer + * + * Deprecated: 4.10 + **/ +GtkCellRenderer * +gtk_cell_renderer_text_new (void) +{ + return g_object_new (GTK_TYPE_CELL_RENDERER_TEXT, NULL); +} + +static inline gboolean +show_placeholder_text (GtkCellRendererText *celltext) +{ + GtkCellRendererTextPrivate *priv = gtk_cell_renderer_text_get_instance_private (celltext); + + return priv->editable && priv->placeholder_text && + (!priv->text || !priv->text[0]); +} + +static void +add_attr (PangoAttrList *attr_list, + PangoAttribute *attr) +{ + attr->start_index = 0; + attr->end_index = G_MAXINT; + + pango_attr_list_insert (attr_list, attr); +} + +static PangoLayout* +get_layout (GtkCellRendererText *celltext, + GtkWidget *widget, + const GdkRectangle *cell_area, + GtkCellRendererState flags) +{ + GtkCellRendererTextPrivate *priv = gtk_cell_renderer_text_get_instance_private (celltext); + PangoAttrList *attr_list; + PangoLayout *layout; + PangoUnderline uline; + int xpad; + gboolean placeholder_layout = show_placeholder_text (celltext); + + layout = gtk_widget_create_pango_layout (widget, placeholder_layout ? + priv->placeholder_text : priv->text); + + gtk_cell_renderer_get_padding (GTK_CELL_RENDERER (celltext), &xpad, NULL); + + if (priv->extra_attrs) + attr_list = pango_attr_list_copy (priv->extra_attrs); + else + attr_list = pango_attr_list_new (); + + pango_layout_set_single_paragraph_mode (layout, priv->single_paragraph); + + if (!placeholder_layout && cell_area) + { + /* Add options that affect appearance but not size */ + + /* note that background doesn't go here, since it affects + * background_area not the PangoLayout area + */ + + if (priv->foreground_set + && (flags & GTK_CELL_RENDERER_SELECTED) == 0) + { + PangoColor color; + guint16 alpha; + + color.red = CLAMP (priv->foreground.red * 65535. + 0.5, 0, 65535); + color.green = CLAMP (priv->foreground.green * 65535. + 0.5, 0, 65535); + color.blue = CLAMP (priv->foreground.blue * 65535. + 0.5, 0, 65535); + alpha = CLAMP (priv->foreground.alpha * 65535. + 0.5, 0, 65535); + + add_attr (attr_list, + pango_attr_foreground_new (color.red, color.green, color.blue)); + + add_attr (attr_list, pango_attr_foreground_alpha_new (alpha)); + } + + if (priv->strikethrough_set) + add_attr (attr_list, pango_attr_strikethrough_new (priv->strikethrough)); + } + else if (placeholder_layout) + { + PangoColor color; + guint16 alpha; + GtkStyleContext *context; + GdkRGBA fg = { 0.5, 0.5, 0.5, 1.0 }; + + context = gtk_widget_get_style_context (widget); + gtk_style_context_lookup_color (context, "placeholder_text_color", &fg); + + color.red = CLAMP (fg.red * 65535. + 0.5, 0, 65535); + color.green = CLAMP (fg.green * 65535. + 0.5, 0, 65535); + color.blue = CLAMP (fg.blue * 65535. + 0.5, 0, 65535); + alpha = CLAMP (fg.alpha * 65535. + 0.5, 0, 65535); + + add_attr (attr_list, + pango_attr_foreground_new (color.red, color.green, color.blue)); + + add_attr (attr_list, pango_attr_foreground_alpha_new (alpha)); + } + + add_attr (attr_list, pango_attr_font_desc_new (priv->font)); + + if (priv->scale_set && + priv->font_scale != 1.0) + add_attr (attr_list, pango_attr_scale_new (priv->font_scale)); + + if (priv->underline_set) + uline = priv->underline_style; + else + uline = PANGO_UNDERLINE_NONE; + + if (priv->language_set) + add_attr (attr_list, pango_attr_language_new (priv->language)); + + if ((flags & GTK_CELL_RENDERER_PRELIT) == GTK_CELL_RENDERER_PRELIT) + { + switch (uline) + { + case PANGO_UNDERLINE_NONE: + uline = PANGO_UNDERLINE_SINGLE; + break; + + case PANGO_UNDERLINE_SINGLE: + uline = PANGO_UNDERLINE_DOUBLE; + break; + + case PANGO_UNDERLINE_SINGLE_LINE: + uline = PANGO_UNDERLINE_DOUBLE_LINE; + break; + + case PANGO_UNDERLINE_DOUBLE_LINE: + case PANGO_UNDERLINE_ERROR_LINE: + break; + + case PANGO_UNDERLINE_DOUBLE: + case PANGO_UNDERLINE_LOW: + case PANGO_UNDERLINE_ERROR: + default: + break; + } + } + + if (uline != PANGO_UNDERLINE_NONE) + add_attr (attr_list, pango_attr_underline_new (priv->underline_style)); + + if (priv->rise_set) + add_attr (attr_list, pango_attr_rise_new (priv->rise)); + + /* Now apply the attributes as they will effect the outcome + * of pango_layout_get_extents() */ + pango_layout_set_attributes (layout, attr_list); + pango_attr_list_unref (attr_list); + + if (priv->ellipsize_set) + pango_layout_set_ellipsize (layout, priv->ellipsize); + else + pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_NONE); + + if (priv->wrap_width != -1) + { + PangoRectangle rect; + int width, text_width; + + pango_layout_get_extents (layout, NULL, &rect); + text_width = rect.width; + + if (cell_area) + width = (cell_area->width - xpad * 2) * PANGO_SCALE; + else + width = priv->wrap_width * PANGO_SCALE; + + width = MIN (width, text_width); + + pango_layout_set_width (layout, width); + pango_layout_set_wrap (layout, priv->wrap_mode); + } + else + { + pango_layout_set_width (layout, -1); + pango_layout_set_wrap (layout, PANGO_WRAP_CHAR); + } + + if (priv->align_set) + pango_layout_set_alignment (layout, priv->align); + else + { + PangoAlignment align; + + if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) + align = PANGO_ALIGN_RIGHT; + else + align = PANGO_ALIGN_LEFT; + + pango_layout_set_alignment (layout, align); + } + + return layout; +} + + +static void +get_size (GtkCellRenderer *cell, + GtkWidget *widget, + const GdkRectangle *cell_area, + PangoLayout *layout, + int *x_offset, + int *y_offset, + int *width, + int *height) +{ + GtkCellRendererText *celltext = GTK_CELL_RENDERER_TEXT (cell); + GtkCellRendererTextPrivate *priv = gtk_cell_renderer_text_get_instance_private (celltext); + PangoRectangle rect; + int xpad, ypad; + int cell_width, cell_height; + float xalign, yalign; + + gtk_cell_renderer_get_padding (cell, &xpad, &ypad); + + if (priv->calc_fixed_height) + { + GtkStyleContext *style_context; + PangoContext *context; + PangoFontMetrics *metrics; + PangoFontDescription *font_desc; + int row_height; + + style_context = gtk_widget_get_style_context (widget); + + font_desc = gtk_css_style_get_pango_font (gtk_style_context_lookup_style (style_context)); + pango_font_description_merge_static (font_desc, priv->font, TRUE); + + if (priv->scale_set) + pango_font_description_set_size (font_desc, + priv->font_scale * pango_font_description_get_size (font_desc)); + + context = gtk_widget_get_pango_context (widget); + + metrics = pango_context_get_metrics (context, + font_desc, + pango_context_get_language (context)); + row_height = (pango_font_metrics_get_ascent (metrics) + + pango_font_metrics_get_descent (metrics)); + pango_font_metrics_unref (metrics); + + pango_font_description_free (font_desc); + + gtk_cell_renderer_get_fixed_size (cell, &cell_width, &cell_height); + + gtk_cell_renderer_set_fixed_size (cell, + cell_width, 2 * ypad + + priv->fixed_height_rows * PANGO_PIXELS (row_height)); + + if (height) + { + *height = cell_height; + height = NULL; + } + priv->calc_fixed_height = FALSE; + if (width == NULL) + return; + } + + pango_layout_get_pixel_extents (layout, NULL, &rect); + + gtk_cell_renderer_get_alignment (cell, &xalign, &yalign); + + rect.height = MIN (rect.height, cell_area->height - 2 * ypad); + rect.width = MIN (rect.width, cell_area->width - 2 * xpad); + + if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) + *x_offset = (1.0 - xalign) * (cell_area->width - (rect.width + (2 * xpad))); + else + *x_offset = xalign * (cell_area->width - (rect.width + (2 * xpad))); + + if ((priv->ellipsize_set && priv->ellipsize != PANGO_ELLIPSIZE_NONE) || priv->wrap_width != -1) + *x_offset = MAX(*x_offset, 0); + + *y_offset = yalign * (cell_area->height - (rect.height + (2 * ypad))); + *y_offset = MAX (*y_offset, 0); + + if (height) + *height = ypad * 2 + rect.height; + + if (width) + *width = xpad * 2 + rect.width; +} + +static void +gtk_cell_renderer_text_snapshot (GtkCellRenderer *cell, + GtkSnapshot *snapshot, + GtkWidget *widget, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + GtkCellRendererState flags) + +{ + GtkCellRendererText *celltext = GTK_CELL_RENDERER_TEXT (cell); + GtkCellRendererTextPrivate *priv = gtk_cell_renderer_text_get_instance_private (celltext); + GtkStyleContext *context; + PangoLayout *layout; + int x_offset = 0; + int y_offset = 0; + int xpad, ypad; + PangoRectangle rect; + + layout = get_layout (celltext, widget, cell_area, flags); + get_size (cell, widget, cell_area, layout, &x_offset, &y_offset, NULL, NULL); + context = gtk_widget_get_style_context (widget); + + if (priv->background_set && (flags & GTK_CELL_RENDERER_SELECTED) == 0) + { + gtk_snapshot_append_color (snapshot, + &priv->background, + &GRAPHENE_RECT_INIT( + background_area->x, background_area->y, + background_area->width, background_area->height + )); + } + + gtk_cell_renderer_get_padding (cell, &xpad, &ypad); + + if (priv->ellipsize_set && priv->ellipsize != PANGO_ELLIPSIZE_NONE) + pango_layout_set_width (layout, + (cell_area->width - x_offset - 2 * xpad) * PANGO_SCALE); + else if (priv->wrap_width == -1) + pango_layout_set_width (layout, -1); + + pango_layout_get_pixel_extents (layout, NULL, &rect); + x_offset = x_offset - rect.x; + + gtk_snapshot_push_clip (snapshot, + &GRAPHENE_RECT_INIT( + cell_area->x, cell_area->y, + cell_area->width, cell_area->height + )); + + gtk_snapshot_render_layout (snapshot, context, + cell_area->x + x_offset + xpad, + cell_area->y + y_offset + ypad, + layout); + + gtk_snapshot_pop (snapshot); + + g_object_unref (layout); +} + +static void +gtk_cell_renderer_text_editing_done (GtkCellEditable *entry, + gpointer data) +{ + GtkCellRendererText *celltext = GTK_CELL_RENDERER_TEXT (data); + GtkCellRendererTextPrivate *priv = gtk_cell_renderer_text_get_instance_private (celltext); + const char *path; + const char *new_text; + gboolean canceled; + + g_clear_object (&priv->entry); + + if (priv->focus_out_id > 0) + { + g_signal_handler_disconnect (entry, priv->focus_out_id); + priv->focus_out_id = 0; + } + + if (priv->entry_menu_popdown_timeout) + { + g_source_remove (priv->entry_menu_popdown_timeout); + priv->entry_menu_popdown_timeout = 0; + } + + g_object_get (entry, + "editing-canceled", &canceled, + NULL); + gtk_cell_renderer_stop_editing (GTK_CELL_RENDERER (data), canceled); + + if (canceled) + return; + + path = g_object_get_data (G_OBJECT (entry), GTK_CELL_RENDERER_TEXT_PATH); + new_text = gtk_editable_get_text (GTK_EDITABLE (entry)); + g_signal_emit (data, text_cell_renderer_signals[EDITED], 0, path, new_text); +} + +static void +gtk_cell_renderer_text_focus_changed (GtkWidget *entry, + GParamSpec *pspec, + gpointer data) +{ + if (gtk_widget_has_focus (entry) || + gtk_widget_has_focus (GTK_WIDGET (gtk_entry_get_text_widget (GTK_ENTRY (entry))))) + return; + + g_object_set (entry, + "editing-canceled", TRUE, + NULL); + gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (entry)); + gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (entry)); +} + +static GtkCellEditable * +gtk_cell_renderer_text_start_editing (GtkCellRenderer *cell, + GdkEvent *event, + GtkWidget *widget, + const char *path, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + GtkCellRendererState flags) +{ + GtkCellRendererText *celltext = GTK_CELL_RENDERER_TEXT (cell); + GtkCellRendererTextPrivate *priv = gtk_cell_renderer_text_get_instance_private (celltext); + float xalign, yalign; + + /* If the cell isn't editable we return NULL. */ + if (!priv->editable) + return NULL; + + gtk_cell_renderer_get_alignment (cell, &xalign, &yalign); + + priv->entry = gtk_entry_new (); + g_object_ref_sink (G_OBJECT (priv->entry)); + + gtk_entry_set_has_frame (GTK_ENTRY (priv->entry), FALSE); + gtk_entry_set_alignment (GTK_ENTRY (priv->entry), xalign); + gtk_editable_set_width_chars (GTK_EDITABLE (priv->entry), 5); + + if (priv->text) + gtk_editable_set_text (GTK_EDITABLE (priv->entry), priv->text); + g_object_set_data_full (G_OBJECT (priv->entry), I_(GTK_CELL_RENDERER_TEXT_PATH), g_strdup (path), g_free); + + gtk_editable_select_region (GTK_EDITABLE (priv->entry), 0, -1); + + priv->in_entry_menu = FALSE; + if (priv->entry_menu_popdown_timeout) + { + g_source_remove (priv->entry_menu_popdown_timeout); + priv->entry_menu_popdown_timeout = 0; + } + + g_signal_connect (priv->entry, "editing-done", + G_CALLBACK (gtk_cell_renderer_text_editing_done), celltext); + priv->focus_out_id = g_signal_connect_after (priv->entry, "notify::has-focus", + G_CALLBACK (gtk_cell_renderer_text_focus_changed), + celltext); + + return GTK_CELL_EDITABLE (priv->entry); +} + +/** + * gtk_cell_renderer_text_set_fixed_height_from_font: + * @renderer: A `GtkCellRendererText` + * @number_of_rows: Number of rows of text each cell renderer is allocated, or -1 + * + * Sets the height of a renderer to explicitly be determined by the “font” and + * “y_pad” property set on it. Further changes in these properties do not + * affect the height, so they must be accompanied by a subsequent call to this + * function. Using this function is inflexible, and should really only be used + * if calculating the size of a cell is too slow (ie, a massive number of cells + * displayed). If @number_of_rows is -1, then the fixed height is unset, and + * the height is determined by the properties again. + * + * Deprecated: 4.10 + **/ +void +gtk_cell_renderer_text_set_fixed_height_from_font (GtkCellRendererText *renderer, + int number_of_rows) +{ + GtkCellRendererTextPrivate *priv = gtk_cell_renderer_text_get_instance_private (renderer); + GtkCellRenderer *cell = GTK_CELL_RENDERER (renderer); + + g_return_if_fail (GTK_IS_CELL_RENDERER_TEXT (renderer)); + g_return_if_fail (number_of_rows == -1 || number_of_rows > 0); + + if (number_of_rows == -1) + { + int width, height; + + gtk_cell_renderer_get_fixed_size (cell, &width, &height); + gtk_cell_renderer_set_fixed_size (cell, width, -1); + } + else + { + priv->fixed_height_rows = number_of_rows; + priv->calc_fixed_height = TRUE; + } +} + +static void +gtk_cell_renderer_text_get_preferred_width (GtkCellRenderer *cell, + GtkWidget *widget, + int *minimum_size, + int *natural_size) +{ + GtkCellRendererText *celltext = GTK_CELL_RENDERER_TEXT (cell); + GtkCellRendererTextPrivate *priv = gtk_cell_renderer_text_get_instance_private (celltext); + PangoLayout *layout; + PangoContext *context; + PangoFontMetrics *metrics; + PangoRectangle rect; + int char_width, text_width, ellipsize_chars, xpad; + int min_width, nat_width; + + /* "width-chars" Hard-coded minimum width: + * - minimum size should be MAX (width-chars, strlen ("...")); + * - natural size should be MAX (width-chars, strlen (label->text)); + * + * "wrap-width" User specified natural wrap width + * - minimum size should be MAX (width-chars, 0) + * - natural size should be MIN (wrap-width, strlen (label->text)) + */ + gtk_cell_renderer_get_padding (cell, &xpad, NULL); + + layout = get_layout (celltext, widget, NULL, 0); + + /* Fetch the length of the complete unwrapped text */ + pango_layout_set_width (layout, -1); + pango_layout_get_extents (layout, NULL, &rect); + text_width = rect.width; + + /* Fetch the average size of a character */ + context = pango_layout_get_context (layout); + metrics = pango_context_get_metrics (context, + pango_context_get_font_description (context), + pango_context_get_language (context)); + + char_width = pango_font_metrics_get_approximate_char_width (metrics); + + pango_font_metrics_unref (metrics); + g_object_unref (layout); + + /* enforce minimum width for ellipsized labels at ~3 chars */ + if (priv->ellipsize_set && priv->ellipsize != PANGO_ELLIPSIZE_NONE) + ellipsize_chars = 3; + else + ellipsize_chars = 0; + + if ((priv->ellipsize_set && priv->ellipsize != PANGO_ELLIPSIZE_NONE) || priv->width_chars > 0) + min_width = xpad * 2 + + MIN (PANGO_PIXELS_CEIL (text_width), + (PANGO_PIXELS (char_width) * MAX (priv->width_chars, ellipsize_chars))); + /* If no width-chars set, minimum for wrapping text will be the wrap-width */ + else if (priv->wrap_width > -1) + min_width = xpad * 2 + rect.x + MIN (PANGO_PIXELS_CEIL (text_width), priv->wrap_width); + else + min_width = xpad * 2 + rect.x + PANGO_PIXELS_CEIL (text_width); + + if (priv->width_chars > 0) + nat_width = xpad * 2 + + MAX ((PANGO_PIXELS (char_width) * priv->width_chars), PANGO_PIXELS_CEIL (text_width)); + else + nat_width = xpad * 2 + PANGO_PIXELS_CEIL (text_width); + + nat_width = MAX (nat_width, min_width); + + if (priv->max_width_chars > 0) + { + int max_width = xpad * 2 + PANGO_PIXELS (char_width) * priv->max_width_chars; + + min_width = MIN (min_width, max_width); + nat_width = MIN (nat_width, max_width); + } + + if (minimum_size) + *minimum_size = min_width; + + if (natural_size) + *natural_size = nat_width; +} + +static void +gtk_cell_renderer_text_get_preferred_height_for_width (GtkCellRenderer *cell, + GtkWidget *widget, + int width, + int *minimum_height, + int *natural_height) +{ + GtkCellRendererText *celltext = GTK_CELL_RENDERER_TEXT (cell); + PangoLayout *layout; + int text_height, xpad, ypad; + + gtk_cell_renderer_get_padding (cell, &xpad, &ypad); + + layout = get_layout (celltext, widget, NULL, 0); + + pango_layout_set_width (layout, (width - xpad * 2) * PANGO_SCALE); + pango_layout_get_pixel_size (layout, NULL, &text_height); + + if (minimum_height) + *minimum_height = text_height + ypad * 2; + + if (natural_height) + *natural_height = text_height + ypad * 2; + + g_object_unref (layout); +} + +static void +gtk_cell_renderer_text_get_preferred_height (GtkCellRenderer *cell, + GtkWidget *widget, + int *minimum_size, + int *natural_size) +{ + int min_width; + + /* Thankfully cell renderers dont rotate, so they only have to do + * height-for-width and not the opposite. Here we have only to return + * the height for the base minimum width of the renderer. + * + * Note this code path won't be followed by GtkTreeView which is + * height-for-width specifically. + */ + gtk_cell_renderer_get_preferred_width (cell, widget, &min_width, NULL); + gtk_cell_renderer_text_get_preferred_height_for_width (cell, widget, min_width, + minimum_size, natural_size); +} + +static void +gtk_cell_renderer_text_get_aligned_area (GtkCellRenderer *cell, + GtkWidget *widget, + GtkCellRendererState flags, + const GdkRectangle *cell_area, + GdkRectangle *aligned_area) +{ + GtkCellRendererText *celltext = GTK_CELL_RENDERER_TEXT (cell); + PangoLayout *layout; + int x_offset = 0; + int y_offset = 0; + + layout = get_layout (celltext, widget, cell_area, flags); + get_size (cell, widget, cell_area, layout, &x_offset, &y_offset, + &aligned_area->width, &aligned_area->height); + + aligned_area->x = cell_area->x + x_offset; + aligned_area->y = cell_area->y + y_offset; + + g_object_unref (layout); +} diff --git a/gtk/deprecated/gtkcellrenderertext.h b/gtk/deprecated/gtkcellrenderertext.h new file mode 100644 index 0000000000..d17d09ea25 --- /dev/null +++ b/gtk/deprecated/gtkcellrenderertext.h @@ -0,0 +1,73 @@ +/* gtkcellrenderertext.h + * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#ifndef __GTK_CELL_RENDERER_TEXT_H__ +#define __GTK_CELL_RENDERER_TEXT_H__ + + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include + + +G_BEGIN_DECLS + + +#define GTK_TYPE_CELL_RENDERER_TEXT (gtk_cell_renderer_text_get_type ()) +#define GTK_CELL_RENDERER_TEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_RENDERER_TEXT, GtkCellRendererText)) +#define GTK_CELL_RENDERER_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_CELL_RENDERER_TEXT, GtkCellRendererTextClass)) +#define GTK_IS_CELL_RENDERER_TEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_RENDERER_TEXT)) +#define GTK_IS_CELL_RENDERER_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_CELL_RENDERER_TEXT)) +#define GTK_CELL_RENDERER_TEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CELL_RENDERER_TEXT, GtkCellRendererTextClass)) + +typedef struct _GtkCellRendererText GtkCellRendererText; +typedef struct _GtkCellRendererTextClass GtkCellRendererTextClass; + +struct _GtkCellRendererText +{ + GtkCellRenderer parent; +}; + +struct _GtkCellRendererTextClass +{ + GtkCellRendererClass parent_class; + + void (* edited) (GtkCellRendererText *cell_renderer_text, + const char *path, + const char *new_text); + + /*< private >*/ + + gpointer padding[8]; +}; + +GDK_AVAILABLE_IN_ALL +GType gtk_cell_renderer_text_get_type (void) G_GNUC_CONST; +GDK_DEPRECATED_IN_4_10 +GtkCellRenderer *gtk_cell_renderer_text_new (void); + +GDK_DEPRECATED_IN_4_10 +void gtk_cell_renderer_text_set_fixed_height_from_font (GtkCellRendererText *renderer, + int number_of_rows); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkCellRendererText, g_object_unref) + +G_END_DECLS + +#endif /* __GTK_CELL_RENDERER_TEXT_H__ */ diff --git a/gtk/deprecated/gtkcellrenderertoggle.c b/gtk/deprecated/gtkcellrenderertoggle.c new file mode 100644 index 0000000000..2dd6cd0d03 --- /dev/null +++ b/gtk/deprecated/gtkcellrenderertoggle.c @@ -0,0 +1,655 @@ +/* gtkcellrenderertoggle.c + * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#include "config.h" + +#include "gtkcellrenderertoggle.h" + +#include "gtkcssnumbervalueprivate.h" +#include "gtkcsstransientnodeprivate.h" +#include "gtkmarshalers.h" +#include "gtkprivate.h" +#include "gtkrendericonprivate.h" +#include "gtksnapshot.h" +#include "gtkstylecontextprivate.h" +#include "gtkwidgetprivate.h" +#include "deprecated/gtktreeprivate.h" + +#include + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + +/** + * GtkCellRendererToggle: + * + * Renders a toggle button in a cell + * + * `GtkCellRendererToggle` renders a toggle button in a cell. The + * button is drawn as a radio or a checkbutton, depending on the + * `GtkCellRendererToggle:radio` property. + * When activated, it emits the `GtkCellRendererToggle::toggled` signal. + */ + + +static void gtk_cell_renderer_toggle_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void gtk_cell_renderer_toggle_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); +static void gtk_cell_renderer_toggle_get_size (GtkCellRendererToggle *self, + GtkWidget *widget, + const GdkRectangle *cell_area, + int *x_offset, + int *y_offset, + int *width, + int *height); +static void gtk_cell_renderer_toggle_snapshot (GtkCellRenderer *cell, + GtkSnapshot *snapshot, + GtkWidget *widget, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + GtkCellRendererState flags); +static gboolean gtk_cell_renderer_toggle_activate (GtkCellRenderer *cell, + GdkEvent *event, + GtkWidget *widget, + const char *path, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + GtkCellRendererState flags); + + +enum { + TOGGLED, + LAST_SIGNAL +}; + +enum { + PROP_0, + PROP_ACTIVATABLE, + PROP_ACTIVE, + PROP_RADIO, + PROP_INCONSISTENT +}; + +static guint toggle_cell_signals[LAST_SIGNAL] = { 0 }; + +typedef struct _GtkCellRendererTogglePrivate GtkCellRendererTogglePrivate; +typedef struct _GtkCellRendererToggleClass GtkCellRendererToggleClass; + +struct _GtkCellRendererToggle +{ + GtkCellRenderer parent; +}; + +struct _GtkCellRendererToggleClass +{ + GtkCellRendererClass parent_class; + + void (* toggled) (GtkCellRendererToggle *cell, + const char *path); +}; + +struct _GtkCellRendererTogglePrivate +{ + guint active : 1; + guint activatable : 1; + guint inconsistent : 1; + guint radio : 1; + GtkCssNode *cssnode; +}; + + +G_DEFINE_TYPE_WITH_PRIVATE (GtkCellRendererToggle, gtk_cell_renderer_toggle, GTK_TYPE_CELL_RENDERER) + + +static void +gtk_cell_renderer_toggle_init (GtkCellRendererToggle *celltoggle) +{ + GtkCellRendererTogglePrivate *priv = gtk_cell_renderer_toggle_get_instance_private (celltoggle); + + priv->activatable = TRUE; + priv->active = FALSE; + priv->radio = FALSE; + + g_object_set (celltoggle, "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL); + gtk_cell_renderer_set_padding (GTK_CELL_RENDERER (celltoggle), 2, 2); + + priv->inconsistent = FALSE; +} + +static GtkSizeRequestMode +gtk_cell_renderer_toggle_get_request_mode (GtkCellRenderer *cell) +{ + return GTK_SIZE_REQUEST_CONSTANT_SIZE; +} + +static void +gtk_cell_renderer_toggle_get_preferred_width (GtkCellRenderer *cell, + GtkWidget *widget, + int *minimum, + int *natural) +{ + int width = 0; + + gtk_cell_renderer_toggle_get_size (GTK_CELL_RENDERER_TOGGLE (cell), widget, + NULL, + NULL, NULL, &width, NULL); + + if (minimum) + *minimum = width; + if (natural) + *natural = width; +} + +static void +gtk_cell_renderer_toggle_get_preferred_height (GtkCellRenderer *cell, + GtkWidget *widget, + int *minimum, + int *natural) +{ + int height = 0; + + gtk_cell_renderer_toggle_get_size (GTK_CELL_RENDERER_TOGGLE (cell), widget, + NULL, + NULL, NULL, NULL, &height); + + if (minimum) + *minimum = height; + if (natural) + *natural = height; +} + +static void +gtk_cell_renderer_toggle_class_init (GtkCellRendererToggleClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (class); + + object_class->get_property = gtk_cell_renderer_toggle_get_property; + object_class->set_property = gtk_cell_renderer_toggle_set_property; + + cell_class->get_request_mode = gtk_cell_renderer_toggle_get_request_mode; + cell_class->get_preferred_width = gtk_cell_renderer_toggle_get_preferred_width; + cell_class->get_preferred_height = gtk_cell_renderer_toggle_get_preferred_height; + cell_class->snapshot = gtk_cell_renderer_toggle_snapshot; + cell_class->activate = gtk_cell_renderer_toggle_activate; + + g_object_class_install_property (object_class, + PROP_ACTIVE, + g_param_spec_boolean ("active", NULL, NULL, + FALSE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + g_object_class_install_property (object_class, + PROP_INCONSISTENT, + g_param_spec_boolean ("inconsistent", NULL, NULL, + FALSE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + g_object_class_install_property (object_class, + PROP_ACTIVATABLE, + g_param_spec_boolean ("activatable", NULL, NULL, + TRUE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + g_object_class_install_property (object_class, + PROP_RADIO, + g_param_spec_boolean ("radio", NULL, NULL, + FALSE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + + /** + * GtkCellRendererToggle::toggled: + * @cell_renderer: the object which received the signal + * @path: string representation of `GtkTreePath` describing the + * event location + * + * The ::toggled signal is emitted when the cell is toggled. + * + * It is the responsibility of the application to update the model + * with the correct value to store at @path. Often this is simply the + * opposite of the value currently stored at @path. + **/ + toggle_cell_signals[TOGGLED] = + g_signal_new (I_("toggled"), + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkCellRendererToggleClass, toggled), + NULL, NULL, + NULL, + G_TYPE_NONE, 1, + G_TYPE_STRING); +} + +static void +gtk_cell_renderer_toggle_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + GtkCellRendererToggle *celltoggle = GTK_CELL_RENDERER_TOGGLE (object); + GtkCellRendererTogglePrivate *priv = gtk_cell_renderer_toggle_get_instance_private (celltoggle); + + switch (param_id) + { + case PROP_ACTIVE: + g_value_set_boolean (value, priv->active); + break; + case PROP_INCONSISTENT: + g_value_set_boolean (value, priv->inconsistent); + break; + case PROP_ACTIVATABLE: + g_value_set_boolean (value, priv->activatable); + break; + case PROP_RADIO: + g_value_set_boolean (value, priv->radio); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + + +static void +gtk_cell_renderer_toggle_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkCellRendererToggle *celltoggle = GTK_CELL_RENDERER_TOGGLE (object); + GtkCellRendererTogglePrivate *priv = gtk_cell_renderer_toggle_get_instance_private (celltoggle); + + switch (param_id) + { + case PROP_ACTIVE: + if (priv->active != g_value_get_boolean (value)) + { + priv->active = g_value_get_boolean (value); + g_object_notify_by_pspec (object, pspec); + } + break; + case PROP_INCONSISTENT: + if (priv->inconsistent != g_value_get_boolean (value)) + { + priv->inconsistent = g_value_get_boolean (value); + g_object_notify_by_pspec (object, pspec); + } + break; + case PROP_ACTIVATABLE: + if (priv->activatable != g_value_get_boolean (value)) + { + priv->activatable = g_value_get_boolean (value); + g_object_notify_by_pspec (object, pspec); + } + break; + case PROP_RADIO: + if (priv->radio != g_value_get_boolean (value)) + { + gtk_cell_renderer_toggle_set_radio (celltoggle, g_value_get_boolean (value)); + g_object_notify_by_pspec (object, pspec); + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +/** + * gtk_cell_renderer_toggle_new: + * + * Creates a new `GtkCellRendererToggle`. Adjust rendering + * parameters using object properties. Object properties can be set + * globally (with g_object_set()). Also, with `GtkTreeViewColumn`, you + * can bind a property to a value in a `GtkTreeModel`. For example, you + * can bind the “active” property on the cell renderer to a boolean value + * in the model, thus causing the check button to reflect the state of + * the model. + * + * Returns: the new cell renderer + * + * Deprecated: 4.10 + **/ +GtkCellRenderer * +gtk_cell_renderer_toggle_new (void) +{ + return g_object_new (GTK_TYPE_CELL_RENDERER_TOGGLE, NULL); +} + +static GtkStyleContext * +gtk_cell_renderer_toggle_save_context (GtkCellRendererToggle *cell, + GtkWidget *widget) +{ + GtkCellRendererTogglePrivate *priv = gtk_cell_renderer_toggle_get_instance_private (cell); + GtkStyleContext *context; + GtkCssNode *cssnode; + + context = gtk_widget_get_style_context (widget); + + cssnode = gtk_css_transient_node_new (gtk_widget_get_css_node (widget)); + if (priv->radio) + gtk_css_node_set_name (cssnode, g_quark_from_static_string ("radio")); + else + gtk_css_node_set_name (cssnode, g_quark_from_static_string ("check")); + gtk_style_context_save_to_node (context, cssnode); + g_object_unref (cssnode); + + return context; +} + +static void +gtk_cell_renderer_toggle_restore_context (GtkCellRendererToggle *cell, + GtkStyleContext *context) +{ + gtk_style_context_restore (context); +} + +static int +calc_indicator_size (GtkStyleContext *context) +{ + GtkCssStyle *style = gtk_style_context_lookup_style (context); + return _gtk_css_number_value_get (style->icon->icon_size, 100); +} + +static void +gtk_cell_renderer_toggle_get_size (GtkCellRendererToggle *self, + GtkWidget *widget, + const GdkRectangle *cell_area, + int *x_offset, + int *y_offset, + int *width, + int *height) +{ + GtkCellRenderer *cell = GTK_CELL_RENDERER (self); + int calc_width; + int calc_height; + int xpad, ypad; + GtkStyleContext *context; + GtkBorder border, padding; + + gtk_cell_renderer_get_padding (cell, &xpad, &ypad); + + context = gtk_cell_renderer_toggle_save_context (self, widget); + gtk_style_context_get_padding (context, &padding); + gtk_style_context_get_border (context, &border); + + calc_width = calc_height = calc_indicator_size (context); + calc_width += xpad * 2 + padding.left + padding.right + border.left + border.right; + calc_height += ypad * 2 + padding.top + padding.bottom + border.top + border.bottom; + + gtk_cell_renderer_toggle_restore_context (self, context); + + if (width) + *width = calc_width; + + if (height) + *height = calc_height; + + if (cell_area) + { + float xalign, yalign; + + gtk_cell_renderer_get_alignment (cell, &xalign, &yalign); + + if (x_offset) + { + *x_offset = ((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) ? + (1.0 - xalign) : xalign) * (cell_area->width - calc_width); + *x_offset = MAX (*x_offset, 0); + } + if (y_offset) + { + *y_offset = yalign * (cell_area->height - calc_height); + *y_offset = MAX (*y_offset, 0); + } + } + else + { + if (x_offset) + *x_offset = 0; + if (y_offset) + *y_offset = 0; + } +} + +static void +gtk_cell_renderer_toggle_snapshot (GtkCellRenderer *cell, + GtkSnapshot *snapshot, + GtkWidget *widget, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + GtkCellRendererState flags) +{ + GtkCellRendererToggle *celltoggle = GTK_CELL_RENDERER_TOGGLE (cell); + GtkCellRendererTogglePrivate *priv = gtk_cell_renderer_toggle_get_instance_private (celltoggle); + GtkStyleContext *context; + int width, height; + int x_offset, y_offset; + int xpad, ypad; + GtkStateFlags state; + GtkBorder padding, border; + + gtk_cell_renderer_toggle_get_size (celltoggle, widget, cell_area, + &x_offset, &y_offset, + &width, &height); + gtk_cell_renderer_get_padding (cell, &xpad, &ypad); + width -= xpad * 2; + height -= ypad * 2; + + if (width <= 0 || height <= 0) + return; + + state = gtk_cell_renderer_get_state (cell, widget, flags); + + if (!priv->activatable) + state |= GTK_STATE_FLAG_INSENSITIVE; + + state &= ~(GTK_STATE_FLAG_INCONSISTENT | GTK_STATE_FLAG_CHECKED); + + if (priv->inconsistent) + state |= GTK_STATE_FLAG_INCONSISTENT; + + if (priv->active) + state |= GTK_STATE_FLAG_CHECKED; + + gtk_snapshot_push_clip (snapshot, + &GRAPHENE_RECT_INIT ( + cell_area->x, cell_area->y, + cell_area->width, cell_area->height + )); + + context = gtk_cell_renderer_toggle_save_context (celltoggle, widget); + gtk_style_context_set_state (context, state); + + gtk_snapshot_render_background (snapshot, context, + cell_area->x + x_offset + xpad, + cell_area->y + y_offset + ypad, + width, height); + gtk_snapshot_render_frame (snapshot, context, + cell_area->x + x_offset + xpad, + cell_area->y + y_offset + ypad, + width, height); + + gtk_style_context_get_padding (context, &padding); + gtk_style_context_get_border (context, &border); + + gtk_snapshot_translate (snapshot, + &GRAPHENE_POINT_INIT (cell_area->x + x_offset + xpad + padding.left + border.left, + cell_area->y + y_offset + ypad + padding.top + border.top)); + gtk_css_style_snapshot_icon (gtk_style_context_lookup_style (context), snapshot, + width - padding.left - padding.right - border.left - border.right, + height - padding.top - padding.bottom - border.top - border.bottom); + + gtk_cell_renderer_toggle_restore_context (celltoggle, context); + gtk_snapshot_pop (snapshot); +} + +static int +gtk_cell_renderer_toggle_activate (GtkCellRenderer *cell, + GdkEvent *event, + GtkWidget *widget, + const char *path, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + GtkCellRendererState flags) +{ + GtkCellRendererToggle *celltoggle = GTK_CELL_RENDERER_TOGGLE (cell); + GtkCellRendererTogglePrivate *priv = gtk_cell_renderer_toggle_get_instance_private (celltoggle); + + if (priv->activatable) + { + g_signal_emit (cell, toggle_cell_signals[TOGGLED], 0, path); + return TRUE; + } + + return FALSE; +} + +/** + * gtk_cell_renderer_toggle_set_radio: + * @toggle: a `GtkCellRendererToggle` + * @radio: %TRUE to make the toggle look like a radio button + * + * If @radio is %TRUE, the cell renderer renders a radio toggle + * (i.e. a toggle in a group of mutually-exclusive toggles). + * If %FALSE, it renders a check toggle (a standalone boolean option). + * This can be set globally for the cell renderer, or changed just + * before rendering each cell in the model (for `GtkTreeView`, you set + * up a per-row setting using `GtkTreeViewColumn` to associate model + * columns with cell renderer properties). + * + * Deprecated: 4.10 + **/ +void +gtk_cell_renderer_toggle_set_radio (GtkCellRendererToggle *toggle, + gboolean radio) +{ + GtkCellRendererTogglePrivate *priv = gtk_cell_renderer_toggle_get_instance_private (toggle); + + g_return_if_fail (GTK_IS_CELL_RENDERER_TOGGLE (toggle)); + + priv->radio = radio; +} + +/** + * gtk_cell_renderer_toggle_get_radio: + * @toggle: a `GtkCellRendererToggle` + * + * Returns whether we’re rendering radio toggles rather than checkboxes. + * + * Returns: %TRUE if we’re rendering radio toggles rather than checkboxes + * + * Deprecated: 4.10 + **/ +gboolean +gtk_cell_renderer_toggle_get_radio (GtkCellRendererToggle *toggle) +{ + GtkCellRendererTogglePrivate *priv = gtk_cell_renderer_toggle_get_instance_private (toggle); + + g_return_val_if_fail (GTK_IS_CELL_RENDERER_TOGGLE (toggle), FALSE); + + return priv->radio; +} + +/** + * gtk_cell_renderer_toggle_get_active: + * @toggle: a `GtkCellRendererToggle` + * + * Returns whether the cell renderer is active. See + * gtk_cell_renderer_toggle_set_active(). + * + * Returns: %TRUE if the cell renderer is active. + * + * Deprecated: 4.10 + **/ +gboolean +gtk_cell_renderer_toggle_get_active (GtkCellRendererToggle *toggle) +{ + GtkCellRendererTogglePrivate *priv = gtk_cell_renderer_toggle_get_instance_private (toggle); + + g_return_val_if_fail (GTK_IS_CELL_RENDERER_TOGGLE (toggle), FALSE); + + return priv->active; +} + +/** + * gtk_cell_renderer_toggle_set_active: + * @toggle: a `GtkCellRendererToggle`. + * @setting: the value to set. + * + * Activates or deactivates a cell renderer. + * + * Deprecated: 4.10 + **/ +void +gtk_cell_renderer_toggle_set_active (GtkCellRendererToggle *toggle, + gboolean setting) +{ + g_return_if_fail (GTK_IS_CELL_RENDERER_TOGGLE (toggle)); + + g_object_set (toggle, "active", setting ? TRUE : FALSE, NULL); +} + +/** + * gtk_cell_renderer_toggle_get_activatable: + * @toggle: a `GtkCellRendererToggle` + * + * Returns whether the cell renderer is activatable. See + * gtk_cell_renderer_toggle_set_activatable(). + * + * Returns: %TRUE if the cell renderer is activatable. + * + * Deprecated: 4.10 + **/ +gboolean +gtk_cell_renderer_toggle_get_activatable (GtkCellRendererToggle *toggle) +{ + GtkCellRendererTogglePrivate *priv = gtk_cell_renderer_toggle_get_instance_private (toggle); + + g_return_val_if_fail (GTK_IS_CELL_RENDERER_TOGGLE (toggle), FALSE); + + return priv->activatable; +} + +/** + * gtk_cell_renderer_toggle_set_activatable: + * @toggle: a `GtkCellRendererToggle`. + * @setting: the value to set. + * + * Makes the cell renderer activatable. + * + * Deprecated: 4.10 + **/ +void +gtk_cell_renderer_toggle_set_activatable (GtkCellRendererToggle *toggle, + gboolean setting) +{ + GtkCellRendererTogglePrivate *priv = gtk_cell_renderer_toggle_get_instance_private (toggle); + + g_return_if_fail (GTK_IS_CELL_RENDERER_TOGGLE (toggle)); + + if (priv->activatable != setting) + { + priv->activatable = setting ? TRUE : FALSE; + g_object_notify (G_OBJECT (toggle), "activatable"); + } +} diff --git a/gtk/deprecated/gtkcellrenderertoggle.h b/gtk/deprecated/gtkcellrenderertoggle.h new file mode 100644 index 0000000000..acea749fd9 --- /dev/null +++ b/gtk/deprecated/gtkcellrenderertoggle.h @@ -0,0 +1,65 @@ +/* gtkcellrenderertoggle.h + * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#ifndef __GTK_CELL_RENDERER_TOGGLE_H__ +#define __GTK_CELL_RENDERER_TOGGLE_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include + + +G_BEGIN_DECLS + + +#define GTK_TYPE_CELL_RENDERER_TOGGLE (gtk_cell_renderer_toggle_get_type ()) +#define GTK_CELL_RENDERER_TOGGLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_RENDERER_TOGGLE, GtkCellRendererToggle)) +#define GTK_IS_CELL_RENDERER_TOGGLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_RENDERER_TOGGLE)) + +typedef struct _GtkCellRendererToggle GtkCellRendererToggle; + + +GDK_AVAILABLE_IN_ALL +GType gtk_cell_renderer_toggle_get_type (void) G_GNUC_CONST; +GDK_DEPRECATED_IN_4_10 +GtkCellRenderer *gtk_cell_renderer_toggle_new (void); + +GDK_DEPRECATED_IN_4_10 +gboolean gtk_cell_renderer_toggle_get_radio (GtkCellRendererToggle *toggle); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_renderer_toggle_set_radio (GtkCellRendererToggle *toggle, + gboolean radio); + +GDK_DEPRECATED_IN_4_10 +gboolean gtk_cell_renderer_toggle_get_active (GtkCellRendererToggle *toggle); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_renderer_toggle_set_active (GtkCellRendererToggle *toggle, + gboolean setting); + +GDK_DEPRECATED_IN_4_10 +gboolean gtk_cell_renderer_toggle_get_activatable (GtkCellRendererToggle *toggle); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_renderer_toggle_set_activatable (GtkCellRendererToggle *toggle, + gboolean setting); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkCellRendererToggle, g_object_unref) + +G_END_DECLS + +#endif /* __GTK_CELL_RENDERER_TOGGLE_H__ */ diff --git a/gtk/deprecated/gtkcellview.c b/gtk/deprecated/gtkcellview.c new file mode 100644 index 0000000000..395baa5ad0 --- /dev/null +++ b/gtk/deprecated/gtkcellview.c @@ -0,0 +1,1204 @@ +/* gtkellview.c + * Copyright (C) 2002, 2003 Kristian Rietveld + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#include "config.h" + +#include "gtkcellview.h" + +#include "gtkbuildable.h" +#include "gtkcelllayout.h" +#include "gtkcellareabox.h" +#include "gtkcellrendererpixbuf.h" +#include "gtkcellrenderertext.h" +#include "gtkorientable.h" +#include "gtkprivate.h" +#include "gtkwidgetprivate.h" + +#include + +#include + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + +/** + * GtkCellView: + * + * A widget displaying a single row of a GtkTreeModel + * + * A `GtkCellView` displays a single row of a `GtkTreeModel` using a `GtkCellArea` + * and `GtkCellAreaContext`. A `GtkCellAreaContext` can be provided to the + * `GtkCellView` at construction time in order to keep the cellview in context + * of a group of cell views, this ensures that the renderers displayed will + * be properly aligned with each other (like the aligned cells in the menus + * of `GtkComboBox`). + * + * `GtkCellView` is `GtkOrientable` in order to decide in which orientation + * the underlying `GtkCellAreaContext` should be allocated. Taking the `GtkComboBox` + * menu as an example, cellviews should be oriented horizontally if the menus are + * listed top-to-bottom and thus all share the same width but may have separate + * individual heights (left-to-right menus should be allocated vertically since + * they all share the same height but may have variable widths). + * + * # CSS nodes + * + * GtkCellView has a single CSS node with name cellview. + */ + +static void gtk_cell_view_constructed (GObject *object); +static void gtk_cell_view_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void gtk_cell_view_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); +static void gtk_cell_view_finalize (GObject *object); +static void gtk_cell_view_dispose (GObject *object); +static void gtk_cell_view_size_allocate (GtkWidget *widget, + int width, + int height, + int baseline); +static void gtk_cell_view_snapshot (GtkWidget *widget, + GtkSnapshot *snapshot); +static void gtk_cell_view_set_value (GtkCellView *cell_view, + GtkCellRenderer *renderer, + const char *property, + GValue *value); +static void gtk_cell_view_set_cell_data (GtkCellView *cell_view); + +/* celllayout */ +static void gtk_cell_view_cell_layout_init (GtkCellLayoutIface *iface); +static GtkCellArea *gtk_cell_view_cell_layout_get_area (GtkCellLayout *layout); + + +/* buildable */ +static void gtk_cell_view_buildable_init (GtkBuildableIface *iface); +static gboolean gtk_cell_view_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const char *tagname, + GtkBuildableParser *parser, + gpointer *data); +static void gtk_cell_view_buildable_custom_tag_end (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const char *tagname, + gpointer data); + +static GtkSizeRequestMode gtk_cell_view_get_request_mode (GtkWidget *widget); +static void gtk_cell_view_measure (GtkWidget *widget, + GtkOrientation orientation, + int for_size, + int *minimum, + int *natural, + int *minimum_baseline, + int *natural_baseline); +static void context_size_changed_cb (GtkCellAreaContext *context, + GParamSpec *pspec, + GtkWidget *view); +static void row_changed_cb (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + GtkCellView *view); + +typedef struct _GtkCellViewClass GtkCellViewClass; +typedef struct _GtkCellViewPrivate GtkCellViewPrivate; + +struct _GtkCellView +{ + GtkWidget parent_instance; +}; + +struct _GtkCellViewClass +{ + GtkWidgetClass parent_class; +}; + +struct _GtkCellViewPrivate +{ + GtkTreeModel *model; + GtkTreeRowReference *displayed_row; + + GtkCellArea *area; + GtkCellAreaContext *context; + + gulong size_changed_id; + gulong row_changed_id; + + GtkOrientation orientation; + + guint draw_sensitive : 1; + guint fit_model : 1; +}; + +static GtkBuildableIface *parent_buildable_iface; + +enum +{ + PROP_0, + PROP_ORIENTATION, + PROP_MODEL, + PROP_CELL_AREA, + PROP_CELL_AREA_CONTEXT, + PROP_DRAW_SENSITIVE, + PROP_FIT_MODEL +}; + +G_DEFINE_TYPE_WITH_CODE (GtkCellView, gtk_cell_view, GTK_TYPE_WIDGET, + G_ADD_PRIVATE (GtkCellView) + G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT, + gtk_cell_view_cell_layout_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, + gtk_cell_view_buildable_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL)) + +static void +gtk_cell_view_class_init (GtkCellViewClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + gobject_class->constructed = gtk_cell_view_constructed; + gobject_class->get_property = gtk_cell_view_get_property; + gobject_class->set_property = gtk_cell_view_set_property; + gobject_class->finalize = gtk_cell_view_finalize; + gobject_class->dispose = gtk_cell_view_dispose; + + widget_class->snapshot = gtk_cell_view_snapshot; + widget_class->size_allocate = gtk_cell_view_size_allocate; + widget_class->get_request_mode = gtk_cell_view_get_request_mode; + widget_class->measure = gtk_cell_view_measure; + + /* properties */ + g_object_class_override_property (gobject_class, PROP_ORIENTATION, "orientation"); + + /** + * GtkCellView:model: + * + * The model for cell view + * + * since 2.10 + */ + g_object_class_install_property (gobject_class, + PROP_MODEL, + g_param_spec_object ("model", NULL, NULL, + GTK_TYPE_TREE_MODEL, + GTK_PARAM_READWRITE)); + + + /** + * GtkCellView:cell-area: + * + * The `GtkCellArea` rendering cells + * + * If no area is specified when creating the cell view with gtk_cell_view_new_with_context() + * a horizontally oriented `GtkCellArea`Box will be used. + * + * since 3.0 + */ + g_object_class_install_property (gobject_class, + PROP_CELL_AREA, + g_param_spec_object ("cell-area", NULL, NULL, + GTK_TYPE_CELL_AREA, + GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + /** + * GtkCellView:cell-area-context: + * + * The `GtkCellAreaContext` used to compute the geometry of the cell view. + * + * A group of cell views can be assigned the same context in order to + * ensure the sizes and cell alignments match across all the views with + * the same context. + * + * `GtkComboBox` menus uses this to assign the same context to all cell views + * in the menu items for a single menu (each submenu creates its own + * context since the size of each submenu does not depend on parent + * or sibling menus). + * + * since 3.0 + */ + g_object_class_install_property (gobject_class, + PROP_CELL_AREA_CONTEXT, + g_param_spec_object ("cell-area-context", NULL, NULL, + GTK_TYPE_CELL_AREA_CONTEXT, + GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + /** + * GtkCellView:draw-sensitive: + * + * Whether all cells should be draw as sensitive for this view regardless + * of the actual cell properties (used to make menus with submenus appear + * sensitive when the items in submenus might be insensitive). + * + * since 3.0 + */ + g_object_class_install_property (gobject_class, + PROP_DRAW_SENSITIVE, + g_param_spec_boolean ("draw-sensitive", NULL, NULL, + FALSE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + /** + * GtkCellView:fit-model: + * + * Whether the view should request enough space to always fit + * the size of every row in the model (used by the combo box to + * ensure the combo box size doesn't change when different items + * are selected). + * + * since 3.0 + */ + g_object_class_install_property (gobject_class, + PROP_FIT_MODEL, + g_param_spec_boolean ("fit-model", NULL, NULL, + FALSE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + gtk_widget_class_set_css_name (widget_class, I_("cellview")); +} + +static void +gtk_cell_view_buildable_add_child (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const char *type) +{ + if (GTK_IS_CELL_RENDERER (child)) + _gtk_cell_layout_buildable_add_child (buildable, builder, child, type); + else + parent_buildable_iface->add_child (buildable, builder, child, type); +} + +static void +gtk_cell_view_buildable_init (GtkBuildableIface *iface) +{ + parent_buildable_iface = g_type_interface_peek_parent (iface); + iface->add_child = gtk_cell_view_buildable_add_child; + iface->custom_tag_start = gtk_cell_view_buildable_custom_tag_start; + iface->custom_tag_end = gtk_cell_view_buildable_custom_tag_end; +} + +static void +gtk_cell_view_cell_layout_init (GtkCellLayoutIface *iface) +{ + iface->get_area = gtk_cell_view_cell_layout_get_area; +} + +static void +gtk_cell_view_constructed (GObject *object) +{ + GtkCellView *view = GTK_CELL_VIEW (object); + GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (view); + + G_OBJECT_CLASS (gtk_cell_view_parent_class)->constructed (object); + + if (!priv->area) + { + priv->area = gtk_cell_area_box_new (); + g_object_ref_sink (priv->area); + } + + if (!priv->context) + priv->context = gtk_cell_area_create_context (priv->area); + + priv->size_changed_id = + g_signal_connect (priv->context, "notify", + G_CALLBACK (context_size_changed_cb), view); +} + +static void +gtk_cell_view_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + GtkCellView *view = GTK_CELL_VIEW (object); + GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (view); + + switch (param_id) + { + case PROP_ORIENTATION: + g_value_set_enum (value, priv->orientation); + break; + case PROP_MODEL: + g_value_set_object (value, priv->model); + break; + case PROP_CELL_AREA: + g_value_set_object (value, priv->area); + break; + case PROP_CELL_AREA_CONTEXT: + g_value_set_object (value, priv->context); + break; + case PROP_DRAW_SENSITIVE: + g_value_set_boolean (value, priv->draw_sensitive); + break; + case PROP_FIT_MODEL: + g_value_set_boolean (value, priv->fit_model); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +gtk_cell_view_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkCellView *view = GTK_CELL_VIEW (object); + GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (view); + GtkCellArea *area; + GtkCellAreaContext *context; + + switch (param_id) + { + case PROP_ORIENTATION: + if (priv->orientation != g_value_get_enum (value)) + { + priv->orientation = g_value_get_enum (value); + if (priv->context) + gtk_cell_area_context_reset (priv->context); + gtk_widget_update_orientation (GTK_WIDGET (object), priv->orientation); + g_object_notify_by_pspec (object, pspec); + } + break; + case PROP_MODEL: + gtk_cell_view_set_model (view, g_value_get_object (value)); + break; + case PROP_CELL_AREA: + /* Construct-only, can only be assigned once */ + area = g_value_get_object (value); + + if (area) + { + if (priv->area != NULL) + { + g_warning ("cell-area has already been set, ignoring construct property"); + g_object_ref_sink (area); + g_object_unref (area); + } + else + priv->area = g_object_ref_sink (area); + } + break; + case PROP_CELL_AREA_CONTEXT: + /* Construct-only, can only be assigned once */ + context = g_value_get_object (value); + + if (context) + { + if (priv->context != NULL) + { + g_warning ("cell-area-context has already been set, ignoring construct property"); + g_object_ref_sink (context); + g_object_unref (context); + } + else + priv->context = g_object_ref (context); + } + break; + + case PROP_DRAW_SENSITIVE: + gtk_cell_view_set_draw_sensitive (view, g_value_get_boolean (value)); + break; + + case PROP_FIT_MODEL: + gtk_cell_view_set_fit_model (view, g_value_get_boolean (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +gtk_cell_view_init (GtkCellView *cellview) +{ + GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (cellview); + + priv->orientation = GTK_ORIENTATION_HORIZONTAL; +} + +static void +gtk_cell_view_finalize (GObject *object) +{ + GtkCellView *cellview = GTK_CELL_VIEW (object); + GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (cellview); + + if (priv->displayed_row) + gtk_tree_row_reference_free (priv->displayed_row); + + G_OBJECT_CLASS (gtk_cell_view_parent_class)->finalize (object); +} + +static void +gtk_cell_view_dispose (GObject *object) +{ + GtkCellView *cellview = GTK_CELL_VIEW (object); + GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (cellview); + + gtk_cell_view_set_model (cellview, NULL); + + g_clear_object (&priv->area); + + if (priv->context) + { + g_signal_handler_disconnect (priv->context, priv->size_changed_id); + + g_object_unref (priv->context); + priv->context = NULL; + priv->size_changed_id = 0; + } + + G_OBJECT_CLASS (gtk_cell_view_parent_class)->dispose (object); +} + +static void +gtk_cell_view_size_allocate (GtkWidget *widget, + int width, + int height, + int baseline) +{ + GtkCellView *cellview = GTK_CELL_VIEW (widget); + GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (cellview); + int alloc_width, alloc_height; + + gtk_cell_area_context_get_allocation (priv->context, &alloc_width, &alloc_height); + + /* The first cell view in context is responsible for allocating the context at + * allocate time (or the cellview has its own context and is not grouped with + * any other cell views) + * + * If the cellview is in "fit model" mode, we assume it's not in context and + * needs to allocate every time. + */ + if (priv->fit_model) + gtk_cell_area_context_allocate (priv->context, width, height); + else if (alloc_width != width && priv->orientation == GTK_ORIENTATION_HORIZONTAL) + gtk_cell_area_context_allocate (priv->context, width, -1); + else if (alloc_height != height && priv->orientation == GTK_ORIENTATION_VERTICAL) + gtk_cell_area_context_allocate (priv->context, -1, height); +} + +static void +gtk_cell_view_request_model (GtkCellView *cellview, + GtkTreeIter *parent, + GtkOrientation orientation, + int for_size, + int *minimum_size, + int *natural_size) +{ + GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (cellview); + GtkTreeIter iter; + gboolean valid; + + if (!priv->model) + return; + + valid = gtk_tree_model_iter_children (priv->model, &iter, parent); + while (valid) + { + int min, nat; + + gtk_cell_area_apply_attributes (priv->area, priv->model, &iter, FALSE, FALSE); + + if (orientation == GTK_ORIENTATION_HORIZONTAL) + { + if (for_size < 0) + gtk_cell_area_get_preferred_width (priv->area, priv->context, + GTK_WIDGET (cellview), &min, &nat); + else + gtk_cell_area_get_preferred_width_for_height (priv->area, priv->context, + GTK_WIDGET (cellview), for_size, &min, &nat); + } + else + { + if (for_size < 0) + gtk_cell_area_get_preferred_height (priv->area, priv->context, + GTK_WIDGET (cellview), &min, &nat); + else + gtk_cell_area_get_preferred_height_for_width (priv->area, priv->context, + GTK_WIDGET (cellview), for_size, &min, &nat); + } + + *minimum_size = MAX (min, *minimum_size); + *natural_size = MAX (nat, *natural_size); + + /* Recurse into children when they exist */ + gtk_cell_view_request_model (cellview, &iter, orientation, for_size, minimum_size, natural_size); + + valid = gtk_tree_model_iter_next (priv->model, &iter); + } +} + +static GtkSizeRequestMode +gtk_cell_view_get_request_mode (GtkWidget *widget) +{ + GtkCellView *cellview = GTK_CELL_VIEW (widget); + GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (cellview); + + return gtk_cell_area_get_request_mode (priv->area); +} + +static void +gtk_cell_view_measure (GtkWidget *widget, + GtkOrientation orientation, + int for_size, + int *minimum, + int *natural, + int *minimum_baseline, + int *natural_baseline) +{ + GtkCellView *cellview = GTK_CELL_VIEW (widget); + GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (cellview); + + g_signal_handler_block (priv->context, priv->size_changed_id); + + if (orientation == GTK_ORIENTATION_HORIZONTAL && for_size == -1) + { + if (priv->fit_model) + { + int min = 0, nat = 0; + gtk_cell_view_request_model (cellview, NULL, GTK_ORIENTATION_HORIZONTAL, -1, &min, &nat); + } + else + { + if (priv->displayed_row) + gtk_cell_view_set_cell_data (cellview); + + gtk_cell_area_get_preferred_width (priv->area, priv->context, widget, NULL, NULL); + } + + gtk_cell_area_context_get_preferred_width (priv->context, minimum, natural); + } + else if (orientation == GTK_ORIENTATION_VERTICAL && for_size == -1) + { + if (priv->fit_model) + { + int min = 0, nat = 0; + gtk_cell_view_request_model (cellview, NULL, GTK_ORIENTATION_VERTICAL, -1, &min, &nat); + } + else + { + if (priv->displayed_row) + gtk_cell_view_set_cell_data (cellview); + + gtk_cell_area_get_preferred_height (priv->area, priv->context, widget, NULL, NULL); + } + + gtk_cell_area_context_get_preferred_height (priv->context, minimum, natural); + } + else if (orientation == GTK_ORIENTATION_HORIZONTAL && for_size >= 0) + { + if (priv->fit_model) + { + int min = 0, nat = 0; + gtk_cell_view_request_model (cellview, NULL, GTK_ORIENTATION_HORIZONTAL, for_size, &min, &nat); + + *minimum = min; + *natural = nat; + } + else + { + if (priv->displayed_row) + gtk_cell_view_set_cell_data (cellview); + + gtk_cell_area_get_preferred_width_for_height (priv->area, priv->context, widget, + for_size, minimum, natural); + } + } + else + { + if (priv->fit_model) + { + int min = 0, nat = 0; + gtk_cell_view_request_model (cellview, NULL, GTK_ORIENTATION_VERTICAL, for_size, &min, &nat); + + *minimum = min; + *natural = nat; + } + else + { + if (priv->displayed_row) + gtk_cell_view_set_cell_data (cellview); + + gtk_cell_area_get_preferred_height_for_width (priv->area, priv->context, widget, + for_size, minimum, natural); + } + } + + g_signal_handler_unblock (priv->context, priv->size_changed_id); +} + +static void +gtk_cell_view_snapshot (GtkWidget *widget, + GtkSnapshot *snapshot) +{ + GtkCellView *cellview = GTK_CELL_VIEW (widget); + GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (cellview); + GdkRectangle area; + GtkCellRendererState state; + + /* render cells */ + area.x = 0; + area.y = 0; + area.width = gtk_widget_get_width (widget); + area.height = gtk_widget_get_height (widget); + + /* set cell data (if available) */ + if (priv->displayed_row) + gtk_cell_view_set_cell_data (cellview); + else if (priv->model) + return; + + if (gtk_widget_get_state_flags (widget) & GTK_STATE_FLAG_PRELIGHT) + state = GTK_CELL_RENDERER_PRELIT; + else + state = 0; + + /* Render the cells */ + gtk_cell_area_snapshot (priv->area, priv->context, + widget, snapshot, &area, &area, state, FALSE); + + +} + +static void +gtk_cell_view_set_cell_data (GtkCellView *cell_view) +{ + GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (cell_view); + GtkTreeIter iter; + GtkTreePath *path; + + g_return_if_fail (priv->displayed_row != NULL); + + path = gtk_tree_row_reference_get_path (priv->displayed_row); + if (!path) + return; + + gtk_tree_model_get_iter (priv->model, &iter, path); + gtk_tree_path_free (path); + + gtk_cell_area_apply_attributes (priv->area, + priv->model, + &iter, FALSE, FALSE); + + if (priv->draw_sensitive) + { + GList *l, *cells = + gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (priv->area)); + + for (l = cells; l; l = l->next) + { + GObject *renderer = l->data; + + g_object_set (renderer, "sensitive", TRUE, NULL); + } + g_list_free (cells); + } +} + +/* GtkCellLayout implementation */ +static GtkCellArea * +gtk_cell_view_cell_layout_get_area (GtkCellLayout *layout) +{ + GtkCellView *cellview = GTK_CELL_VIEW (layout); + GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (cellview); + + if (G_UNLIKELY (!priv->area)) + { + priv->area = gtk_cell_area_box_new (); + g_object_ref_sink (priv->area); + } + + return priv->area; +} + +/* GtkBuildable implementation */ +static gboolean +gtk_cell_view_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const char *tagname, + GtkBuildableParser *parser, + gpointer *data) +{ + if (parent_buildable_iface->custom_tag_start && + parent_buildable_iface->custom_tag_start (buildable, builder, child, + tagname, parser, data)) + return TRUE; + + return _gtk_cell_layout_buildable_custom_tag_start (buildable, builder, child, + tagname, parser, data); +} + +static void +gtk_cell_view_buildable_custom_tag_end (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const char *tagname, + gpointer data) +{ + if (_gtk_cell_layout_buildable_custom_tag_end (buildable, builder, child, tagname, + data)) + return; + else if (parent_buildable_iface->custom_tag_end) + parent_buildable_iface->custom_tag_end (buildable, builder, child, tagname, + data); +} + +static void +context_size_changed_cb (GtkCellAreaContext *context, + GParamSpec *pspec, + GtkWidget *view) +{ + if (!strcmp (pspec->name, "minimum-width") || + !strcmp (pspec->name, "natural-width") || + !strcmp (pspec->name, "minimum-height") || + !strcmp (pspec->name, "natural-height")) + gtk_widget_queue_resize (view); +} + +static void +row_changed_cb (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + GtkCellView *view) +{ + GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (view); + GtkTreePath *row_path; + + if (priv->displayed_row) + { + row_path = gtk_tree_row_reference_get_path (priv->displayed_row); + + if (row_path) + { + /* Resize everything in our context if our row changed */ + if (gtk_tree_path_compare (row_path, path) == 0) + gtk_cell_area_context_reset (priv->context); + + gtk_tree_path_free (row_path); + } + } +} + +/** + * gtk_cell_view_new: + * + * Creates a new `GtkCellView` widget. + * + * Returns: A newly created `GtkCellView` widget. + * + * Deprecated: 4.10 + */ +GtkWidget * +gtk_cell_view_new (void) +{ + GtkCellView *cellview; + + cellview = g_object_new (GTK_TYPE_CELL_VIEW, NULL); + + return GTK_WIDGET (cellview); +} + + +/** + * gtk_cell_view_new_with_context: + * @area: the `GtkCellArea` to layout cells + * @context: the `GtkCellAreaContext` in which to calculate cell geometry + * + * Creates a new `GtkCellView` widget with a specific `GtkCellArea` + * to layout cells and a specific `GtkCellAreaContext`. + * + * Specifying the same context for a handful of cells lets + * the underlying area synchronize the geometry for those cells, + * in this way alignments with cellviews for other rows are + * possible. + * + * Returns: A newly created `GtkCellView` widget. + * + * Deprecated: 4.10 + */ +GtkWidget * +gtk_cell_view_new_with_context (GtkCellArea *area, + GtkCellAreaContext *context) +{ + g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL); + g_return_val_if_fail (context == NULL || GTK_IS_CELL_AREA_CONTEXT (context), NULL); + + return (GtkWidget *)g_object_new (GTK_TYPE_CELL_VIEW, + "cell-area", area, + "cell-area-context", context, + NULL); +} + +/** + * gtk_cell_view_new_with_text: + * @text: the text to display in the cell view + * + * Creates a new `GtkCellView` widget, adds a `GtkCellRendererText` + * to it, and makes it show @text. + * + * Returns: A newly created `GtkCellView` widget. + * + * Deprecated: 4.10 + */ +GtkWidget * +gtk_cell_view_new_with_text (const char *text) +{ + GtkCellView *cellview; + GtkCellRenderer *renderer; + GValue value = G_VALUE_INIT; + + cellview = GTK_CELL_VIEW (gtk_cell_view_new ()); + + renderer = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (cellview), + renderer, TRUE); + + g_value_init (&value, G_TYPE_STRING); + g_value_set_string (&value, text); + gtk_cell_view_set_value (cellview, renderer, "text", &value); + g_value_unset (&value); + + return GTK_WIDGET (cellview); +} + +/** + * gtk_cell_view_new_with_markup: + * @markup: the text to display in the cell view + * + * Creates a new `GtkCellView` widget, adds a `GtkCellRendererText` + * to it, and makes it show @markup. The text can be marked up with + * the [Pango text markup language](https://docs.gtk.org/Pango/pango_markup.html). + * + * Returns: A newly created `GtkCellView` widget. + * + * Deprecated: 4.10 + */ +GtkWidget * +gtk_cell_view_new_with_markup (const char *markup) +{ + GtkCellView *cellview; + GtkCellRenderer *renderer; + GValue value = G_VALUE_INIT; + + cellview = GTK_CELL_VIEW (gtk_cell_view_new ()); + + renderer = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (cellview), + renderer, TRUE); + + g_value_init (&value, G_TYPE_STRING); + g_value_set_string (&value, markup); + gtk_cell_view_set_value (cellview, renderer, "markup", &value); + g_value_unset (&value); + + return GTK_WIDGET (cellview); +} + +/** + * gtk_cell_view_new_with_texture: + * @texture: the image to display in the cell view + * + * Creates a new `GtkCellView` widget, adds a `GtkCellRendererPixbuf` + * to it, and makes it show @texture. + * + * Returns: A newly created `GtkCellView` widget. + * + * Deprecated: 4.10 + */ +GtkWidget * +gtk_cell_view_new_with_texture (GdkTexture *texture) +{ + GtkCellView *cellview; + GtkCellRenderer *renderer; + GValue value = G_VALUE_INIT; + + cellview = GTK_CELL_VIEW (gtk_cell_view_new ()); + + renderer = gtk_cell_renderer_pixbuf_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (cellview), + renderer, TRUE); + + g_value_init (&value, GDK_TYPE_TEXTURE); + g_value_set_object (&value, texture); + gtk_cell_view_set_value (cellview, renderer, "texture", &value); + g_value_unset (&value); + + return GTK_WIDGET (cellview); +} + +/** + * gtk_cell_view_set_value: + * @cell_view: a `GtkCellView` widget + * @renderer: one of the renderers of @cell_view + * @property: the name of the property of @renderer to set + * @value: the new value to set the property to + * + * Sets a property of a cell renderer of @cell_view, and + * makes sure the display of @cell_view is updated. + * + * Deprecated: 4.10 + */ +static void +gtk_cell_view_set_value (GtkCellView *cell_view, + GtkCellRenderer *renderer, + const char *property, + GValue *value) +{ + g_object_set_property (G_OBJECT (renderer), property, value); + + /* force resize and redraw */ + gtk_widget_queue_resize (GTK_WIDGET (cell_view)); + gtk_widget_queue_draw (GTK_WIDGET (cell_view)); +} + +/** + * gtk_cell_view_set_model: + * @cell_view: a `GtkCellView` + * @model: (nullable): a `GtkTreeModel` + * + * Sets the model for @cell_view. If @cell_view already has a model + * set, it will remove it before setting the new model. If @model is + * %NULL, then it will unset the old model. + * + * Deprecated: 4.10 + */ +void +gtk_cell_view_set_model (GtkCellView *cell_view, + GtkTreeModel *model) +{ + GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (cell_view); + + g_return_if_fail (GTK_IS_CELL_VIEW (cell_view)); + g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model)); + + if (priv->model) + { + g_signal_handler_disconnect (priv->model, priv->row_changed_id); + priv->row_changed_id = 0; + + if (priv->displayed_row) + gtk_tree_row_reference_free (priv->displayed_row); + priv->displayed_row = NULL; + + g_object_unref (priv->model); + } + + priv->model = model; + + if (priv->model) + { + g_object_ref (priv->model); + + priv->row_changed_id = + g_signal_connect (priv->model, "row-changed", + G_CALLBACK (row_changed_cb), cell_view); + } +} + +/** + * gtk_cell_view_get_model: + * @cell_view: a `GtkCellView` + * + * Returns the model for @cell_view. If no model is used %NULL is + * returned. + * + * Returns: (nullable) (transfer none): a `GtkTreeModel` used + * + * Deprecated: 4.10 + */ +GtkTreeModel * +gtk_cell_view_get_model (GtkCellView *cell_view) +{ + GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (cell_view); + + g_return_val_if_fail (GTK_IS_CELL_VIEW (cell_view), NULL); + + return priv->model; +} + +/** + * gtk_cell_view_set_displayed_row: + * @cell_view: a `GtkCellView` + * @path: (nullable): a `GtkTreePath` or %NULL to unset. + * + * Sets the row of the model that is currently displayed + * by the `GtkCellView`. If the path is unset, then the + * contents of the cellview “stick” at their last value; + * this is not normally a desired result, but may be + * a needed intermediate state if say, the model for + * the `GtkCellView` becomes temporarily empty. + * + * Deprecated: 4.10 + **/ +void +gtk_cell_view_set_displayed_row (GtkCellView *cell_view, + GtkTreePath *path) +{ + GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (cell_view); + + g_return_if_fail (GTK_IS_CELL_VIEW (cell_view)); + g_return_if_fail (GTK_IS_TREE_MODEL (priv->model)); + + if (priv->displayed_row) + gtk_tree_row_reference_free (priv->displayed_row); + + if (path) + priv->displayed_row = gtk_tree_row_reference_new (priv->model, path); + else + priv->displayed_row = NULL; + + /* force resize and redraw */ + gtk_widget_queue_resize (GTK_WIDGET (cell_view)); + gtk_widget_queue_draw (GTK_WIDGET (cell_view)); +} + +/** + * gtk_cell_view_get_displayed_row: + * @cell_view: a `GtkCellView` + * + * Returns a `GtkTreePath` referring to the currently + * displayed row. If no row is currently displayed, + * %NULL is returned. + * + * Returns: (nullable) (transfer full): the currently displayed row + * + * Deprecated: 4.10 + */ +GtkTreePath * +gtk_cell_view_get_displayed_row (GtkCellView *cell_view) +{ + GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (cell_view); + + g_return_val_if_fail (GTK_IS_CELL_VIEW (cell_view), NULL); + + if (!priv->displayed_row) + return NULL; + + return gtk_tree_row_reference_get_path (priv->displayed_row); +} + +/** + * gtk_cell_view_get_draw_sensitive: + * @cell_view: a `GtkCellView` + * + * Gets whether @cell_view is configured to draw all of its + * cells in a sensitive state. + * + * Returns: whether @cell_view draws all of its + * cells in a sensitive state + * + * Deprecated: 4.10 + */ +gboolean +gtk_cell_view_get_draw_sensitive (GtkCellView *cell_view) +{ + GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (cell_view); + + g_return_val_if_fail (GTK_IS_CELL_VIEW (cell_view), FALSE); + + return priv->draw_sensitive; +} + +/** + * gtk_cell_view_set_draw_sensitive: + * @cell_view: a `GtkCellView` + * @draw_sensitive: whether to draw all cells in a sensitive state. + * + * Sets whether @cell_view should draw all of its + * cells in a sensitive state, this is used by `GtkComboBox` menus + * to ensure that rows with insensitive cells that contain + * children appear sensitive in the parent menu item. + * + * Deprecated: 4.10 + */ +void +gtk_cell_view_set_draw_sensitive (GtkCellView *cell_view, + gboolean draw_sensitive) +{ + GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (cell_view); + + g_return_if_fail (GTK_IS_CELL_VIEW (cell_view)); + + if (priv->draw_sensitive != draw_sensitive) + { + priv->draw_sensitive = draw_sensitive; + + g_object_notify (G_OBJECT (cell_view), "draw-sensitive"); + } +} + +/** + * gtk_cell_view_get_fit_model: + * @cell_view: a `GtkCellView` + * + * Gets whether @cell_view is configured to request space + * to fit the entire `GtkTreeModel`. + * + * Returns: whether @cell_view requests space to fit + * the entire `GtkTreeModel`. + * + * Deprecated: 4.10 + */ +gboolean +gtk_cell_view_get_fit_model (GtkCellView *cell_view) +{ + GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (cell_view); + + g_return_val_if_fail (GTK_IS_CELL_VIEW (cell_view), FALSE); + + return priv->fit_model; +} + +/** + * gtk_cell_view_set_fit_model: + * @cell_view: a `GtkCellView` + * @fit_model: whether @cell_view should request space for the whole model. + * + * Sets whether @cell_view should request space to fit the entire `GtkTreeModel`. + * + * This is used by `GtkComboBox` to ensure that the cell view displayed on + * the combo box’s button always gets enough space and does not resize + * when selection changes. + * + * Deprecated: 4.10 + */ +void +gtk_cell_view_set_fit_model (GtkCellView *cell_view, + gboolean fit_model) +{ + GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (cell_view); + + g_return_if_fail (GTK_IS_CELL_VIEW (cell_view)); + + if (priv->fit_model != fit_model) + { + priv->fit_model = fit_model; + + gtk_cell_area_context_reset (priv->context); + + g_object_notify (G_OBJECT (cell_view), "fit-model"); + } +} diff --git a/gtk/deprecated/gtkcellview.h b/gtk/deprecated/gtkcellview.h new file mode 100644 index 0000000000..796d26de2d --- /dev/null +++ b/gtk/deprecated/gtkcellview.h @@ -0,0 +1,77 @@ +/* gtkcellview.h + * Copyright (C) 2002, 2003 Kristian Rietveld + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#ifndef __GTK_CELL_VIEW_H__ +#define __GTK_CELL_VIEW_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include +#include +#include +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_CELL_VIEW (gtk_cell_view_get_type ()) +#define GTK_CELL_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_VIEW, GtkCellView)) +#define GTK_IS_CELL_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_VIEW)) + +typedef struct _GtkCellView GtkCellView; + +GDK_AVAILABLE_IN_ALL +GType gtk_cell_view_get_type (void) G_GNUC_CONST; +GDK_DEPRECATED_IN_4_10 +GtkWidget *gtk_cell_view_new (void); +GDK_DEPRECATED_IN_4_10 +GtkWidget *gtk_cell_view_new_with_context (GtkCellArea *area, + GtkCellAreaContext *context); +GDK_DEPRECATED_IN_4_10 +GtkWidget *gtk_cell_view_new_with_text (const char *text); +GDK_DEPRECATED_IN_4_10 +GtkWidget *gtk_cell_view_new_with_markup (const char *markup); +GDK_DEPRECATED_IN_4_10 +GtkWidget *gtk_cell_view_new_with_texture (GdkTexture *texture); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_view_set_model (GtkCellView *cell_view, + GtkTreeModel *model); +GDK_DEPRECATED_IN_4_10 +GtkTreeModel *gtk_cell_view_get_model (GtkCellView *cell_view); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_view_set_displayed_row (GtkCellView *cell_view, + GtkTreePath *path); +GDK_DEPRECATED_IN_4_10 +GtkTreePath *gtk_cell_view_get_displayed_row (GtkCellView *cell_view); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_cell_view_get_draw_sensitive (GtkCellView *cell_view); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_view_set_draw_sensitive (GtkCellView *cell_view, + gboolean draw_sensitive); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_cell_view_get_fit_model (GtkCellView *cell_view); +GDK_DEPRECATED_IN_4_10 +void gtk_cell_view_set_fit_model (GtkCellView *cell_view, + gboolean fit_model); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkCellView, g_object_unref) + +G_END_DECLS + +#endif /* __GTK_CELL_VIEW_H__ */ diff --git a/gtk/deprecated/gtkcombobox.c b/gtk/deprecated/gtkcombobox.c new file mode 100644 index 0000000000..6283a644d5 --- /dev/null +++ b/gtk/deprecated/gtkcombobox.c @@ -0,0 +1,3074 @@ +/* gtkcombobox.c + * Copyright (C) 2002, 2003 Kristian Rietveld + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#include "config.h" + +#include "gtkcomboboxprivate.h" + +#include "gtkbox.h" +#include "gtkcellareabox.h" +#include "gtkcelllayout.h" +#include "gtkcellrenderertext.h" +#include "gtkcellview.h" +#include "gtkeventcontrollerkey.h" +#include "gtkeventcontrollerscroll.h" +#include "gtkframe.h" +#include "gtkbuiltiniconprivate.h" +#include "gtkliststore.h" +#include "gtkmain.h" +#include "gtkmarshalers.h" +#include "gtkprivate.h" +#include "gtkshortcutcontroller.h" +#include "gtktogglebutton.h" +#include "gtktreepopoverprivate.h" +#include "gtktypebuiltins.h" +#include "gtkwidgetprivate.h" + +#include +#include +#include + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + +/** + * GtkComboBox: + * + * A `GtkComboBox` is a widget that allows the user to choose from a list of + * valid choices. + * + * ![An example GtkComboBox](combo-box.png) + * + * The `GtkComboBox` displays the selected choice; when activated, the + * `GtkComboBox` displays a popup which allows the user to make a new choice. + * + * The `GtkComboBox` uses the model-view pattern; the list of valid choices + * is specified in the form of a tree model, and the display of the choices + * can be adapted to the data in the model by using cell renderers, as you + * would in a tree view. This is possible since `GtkComboBox` implements the + * [iface@Gtk.CellLayout] interface. The tree model holding the valid + * choices is not restricted to a flat list, it can be a real tree, and the + * popup will reflect the tree structure. + * + * To allow the user to enter values not in the model, the + * [property@Gtk.ComboBox:has-entry] property allows the `GtkComboBox` to + * contain a [class@Gtk.Entry]. This entry can be accessed by calling + * [method@Gtk.ComboBox.get_child] on the combo box. + * + * For a simple list of textual choices, the model-view API of `GtkComboBox` + * can be a bit overwhelming. In this case, [class@Gtk.ComboBoxText] offers + * a simple alternative. Both `GtkComboBox` and `GtkComboBoxText` can contain + * an entry. + * + * ## CSS nodes + * + * ``` + * combobox + * ├── box.linked + * │ ╰── button.combo + * │ ╰── box + * │ ├── cellview + * │ ╰── arrow + * ╰── window.popup + * ``` + * + * A normal combobox contains a box with the .linked class, a button + * with the .combo class and inside those buttons, there are a cellview and + * an arrow. + * + * ``` + * combobox + * ├── box.linked + * │ ├── entry.combo + * │ ╰── button.combo + * │ ╰── box + * │ ╰── arrow + * ╰── window.popup + * ``` + * + * A `GtkComboBox` with an entry has a single CSS node with name combobox. + * It contains a box with the .linked class. That box contains an entry and + * a button, both with the .combo class added. The button also contains another + * node with name arrow. + * + * # Accessibility + * + * `GtkComboBox` uses the %GTK_ACCESSIBLE_ROLE_COMBO_BOX role. + */ + +typedef struct +{ + GtkWidget *child; + + GtkTreeModel *model; + + GtkCellArea *area; + + int active; /* Only temporary */ + GtkTreeRowReference *active_row; + + GtkWidget *cell_view; + + GtkWidget *box; + GtkWidget *button; + GtkWidget *arrow; + + GtkWidget *popup_widget; + + guint popup_idle_id; + guint scroll_timer; + guint resize_idle_id; + + /* For "has-entry" specific behavior we track + * an automated cell renderer and text column + */ + int text_column; + GtkCellRenderer *text_renderer; + + int id_column; + + guint popup_in_progress : 1; + guint popup_shown : 1; + guint has_frame : 1; + guint is_cell_renderer : 1; + guint editing_canceled : 1; + guint auto_scroll : 1; + guint button_sensitivity : 2; + guint has_entry : 1; + guint popup_fixed_width : 1; + + GtkTreeViewRowSeparatorFunc row_separator_func; + gpointer row_separator_data; + GDestroyNotify row_separator_destroy; +} GtkComboBoxPrivate; + +/* There are 2 modes to this widget, which can be characterized as follows: + * + * 1) no child added: + * + * cell_view -> GtkCellView, regular child + * button -> GtkToggleButton set_parent to combo + * arrow -> GtkArrow set_parent to button + * popup_widget -> GtkMenu + * + * 2) child added: + * + * cell_view -> NULL + * button -> GtkToggleButton set_parent to combo + * arrow -> GtkArrow, child of button + * popup_widget -> GtkMenu + */ + +enum { + ACTIVATE, + CHANGED, + MOVE_ACTIVE, + POPUP, + POPDOWN, + FORMAT_ENTRY_TEXT, + LAST_SIGNAL +}; + +enum { + PROP_0, + PROP_MODEL, + PROP_ACTIVE, + PROP_HAS_FRAME, + PROP_POPUP_SHOWN, + PROP_BUTTON_SENSITIVITY, + PROP_EDITING_CANCELED, + PROP_HAS_ENTRY, + PROP_ENTRY_TEXT_COLUMN, + PROP_POPUP_FIXED_WIDTH, + PROP_ID_COLUMN, + PROP_ACTIVE_ID, + PROP_CHILD +}; + +static guint combo_box_signals[LAST_SIGNAL] = {0,}; + +/* common */ + +static void gtk_combo_box_cell_layout_init (GtkCellLayoutIface *iface); +static void gtk_combo_box_cell_editable_init (GtkCellEditableIface *iface); +static void gtk_combo_box_constructed (GObject *object); +static void gtk_combo_box_dispose (GObject *object); +static void gtk_combo_box_unmap (GtkWidget *widget); + +static void gtk_combo_box_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *spec); +static void gtk_combo_box_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *spec); + +static gboolean gtk_combo_box_grab_focus (GtkWidget *widget); +static void gtk_combo_box_button_toggled (GtkWidget *widget, + gpointer data); + +static void gtk_combo_box_menu_show (GtkWidget *menu, + gpointer user_data); +static void gtk_combo_box_menu_hide (GtkWidget *menu, + gpointer user_data); + +static void gtk_combo_box_unset_model (GtkComboBox *combo_box); + +static void gtk_combo_box_set_active_internal (GtkComboBox *combo_box, + GtkTreePath *path); + +static void gtk_combo_box_real_move_active (GtkComboBox *combo_box, + GtkScrollType scroll); +static void gtk_combo_box_real_popup (GtkComboBox *combo_box); +static gboolean gtk_combo_box_real_popdown (GtkComboBox *combo_box); + +static gboolean gtk_combo_box_scroll_controller_scroll (GtkEventControllerScroll *scroll, + double dx, + double dy, + GtkComboBox *combo_box); + +/* listening to the model */ +static void gtk_combo_box_model_row_inserted (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer user_data); +static void gtk_combo_box_model_row_deleted (GtkTreeModel *model, + GtkTreePath *path, + gpointer user_data); +static void gtk_combo_box_model_rows_reordered (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + int *new_order, + gpointer user_data); +static void gtk_combo_box_model_row_changed (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data); + +static void gtk_combo_box_menu_activate (GtkWidget *menu, + const char *path, + GtkComboBox *combo_box); +static void gtk_combo_box_update_sensitivity (GtkComboBox *combo_box); +static gboolean gtk_combo_box_menu_key (GtkEventControllerKey *key, + guint keyval, + guint keycode, + GdkModifierType modifiers, + GtkComboBox *combo_box); +static void gtk_combo_box_menu_popup (GtkComboBox *combo_box); + +/* cell layout */ +static GtkCellArea *gtk_combo_box_cell_layout_get_area (GtkCellLayout *cell_layout); + +static gboolean gtk_combo_box_mnemonic_activate (GtkWidget *widget, + gboolean group_cycling); + +static void gtk_combo_box_child_show (GtkWidget *widget, + GtkComboBox *combo_box); +static void gtk_combo_box_child_hide (GtkWidget *widget, + GtkComboBox *combo_box); + +/* GtkComboBox:has-entry callbacks */ +static void gtk_combo_box_entry_contents_changed (GtkEntry *entry, + gpointer user_data); +static void gtk_combo_box_entry_active_changed (GtkComboBox *combo_box, + gpointer user_data); +static char *gtk_combo_box_format_entry_text (GtkComboBox *combo_box, + const char *path); + +/* GtkBuildable method implementation */ +static GtkBuildableIface *parent_buildable_iface; + +static void gtk_combo_box_buildable_init (GtkBuildableIface *iface); +static void gtk_combo_box_buildable_add_child (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const char *type); +static gboolean gtk_combo_box_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const char *tagname, + GtkBuildableParser *parser, + gpointer *data); +static void gtk_combo_box_buildable_custom_tag_end (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const char *tagname, + gpointer data); +static GObject *gtk_combo_box_buildable_get_internal_child (GtkBuildable *buildable, + GtkBuilder *builder, + const char *childname); + + + +/* GtkCellEditable method implementations */ +static void gtk_combo_box_start_editing (GtkCellEditable *cell_editable, + GdkEvent *event); + +G_DEFINE_TYPE_WITH_CODE (GtkComboBox, gtk_combo_box, GTK_TYPE_WIDGET, + G_ADD_PRIVATE (GtkComboBox) + G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT, + gtk_combo_box_cell_layout_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_EDITABLE, + gtk_combo_box_cell_editable_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, + gtk_combo_box_buildable_init)) + + +/* common */ +static void +gtk_combo_box_measure (GtkWidget *widget, + GtkOrientation orientation, + int size, + int *minimum, + int *natural, + int *minimum_baseline, + int *natural_baseline) +{ + GtkComboBox *combo_box = GTK_COMBO_BOX (widget); + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + + gtk_widget_measure (priv->box, + orientation, + size, + minimum, natural, + minimum_baseline, natural_baseline); +} + +static void +gtk_combo_box_activate (GtkComboBox *combo_box) +{ + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + + gtk_widget_activate (priv->button); +} + +static void +gtk_combo_box_size_allocate (GtkWidget *widget, + int width, + int height, + int baseline) +{ + GtkComboBox *combo_box = GTK_COMBO_BOX (widget); + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + int menu_width; + + gtk_widget_size_allocate (priv->box, + &(GtkAllocation) { + 0, 0, + width, height + }, baseline); + + gtk_widget_set_size_request (priv->popup_widget, -1, -1); + + if (priv->popup_fixed_width) + gtk_widget_measure (priv->popup_widget, GTK_ORIENTATION_HORIZONTAL, -1, + &menu_width, NULL, NULL, NULL); + else + gtk_widget_measure (priv->popup_widget, GTK_ORIENTATION_HORIZONTAL, -1, + NULL, &menu_width, NULL, NULL); + + gtk_widget_set_size_request (priv->popup_widget, + MAX (width, menu_width), -1); + + gtk_popover_present (GTK_POPOVER (priv->popup_widget)); +} + +static void +gtk_combo_box_compute_expand (GtkWidget *widget, + gboolean *hexpand, + gboolean *vexpand) +{ + GtkComboBox *combo_box = GTK_COMBO_BOX (widget); + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + GtkWidget *child = priv->child; + + if (child && child != priv->cell_view) + { + *hexpand = gtk_widget_compute_expand (child, GTK_ORIENTATION_HORIZONTAL); + *vexpand = gtk_widget_compute_expand (child, GTK_ORIENTATION_VERTICAL); + } + else + { + *hexpand = FALSE; + *vexpand = FALSE; + } +} + +static void +gtk_combo_box_class_init (GtkComboBoxClass *klass) +{ + GObjectClass *object_class; + GtkWidgetClass *widget_class; + + widget_class = (GtkWidgetClass *)klass; + widget_class->mnemonic_activate = gtk_combo_box_mnemonic_activate; + widget_class->grab_focus = gtk_combo_box_grab_focus; + widget_class->focus = gtk_widget_focus_child; + widget_class->measure = gtk_combo_box_measure; + widget_class->size_allocate = gtk_combo_box_size_allocate; + widget_class->unmap = gtk_combo_box_unmap; + widget_class->compute_expand = gtk_combo_box_compute_expand; + + object_class = (GObjectClass *)klass; + object_class->constructed = gtk_combo_box_constructed; + object_class->dispose = gtk_combo_box_dispose; + object_class->set_property = gtk_combo_box_set_property; + object_class->get_property = gtk_combo_box_get_property; + + klass->activate = gtk_combo_box_activate; + klass->format_entry_text = gtk_combo_box_format_entry_text; + + /* signals */ + /** + * GtkComboBox::activate: + * @widget: the object which received the signal. + * + * Emitted to when the combo box is activated. + * + * The `::activate` signal on `GtkComboBox` is an action signal and + * emitting it causes the combo box to pop up its dropdown. + * + * Since: 4.6 + */ + combo_box_signals[ACTIVATE] = + g_signal_new (I_ ("activate"), + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkComboBoxClass, activate), + NULL, NULL, + NULL, + G_TYPE_NONE, 0); + + gtk_widget_class_set_activate_signal (widget_class, combo_box_signals[ACTIVATE]); + + /** + * GtkComboBox::changed: + * @widget: the object which received the signal + * + * Emitted when the active item is changed. + * + * The can be due to the user selecting a different item from the list, + * or due to a call to [method@Gtk.ComboBox.set_active_iter]. It will + * also be emitted while typing into the entry of a combo box with an entry. + */ + combo_box_signals[CHANGED] = + g_signal_new (I_("changed"), + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkComboBoxClass, changed), + NULL, NULL, + NULL, + G_TYPE_NONE, 0); + + /** + * GtkComboBox::move-active: + * @widget: the object that received the signal + * @scroll_type: a `GtkScrollType` + * + * Emitted to move the active selection. + * + * This is an [keybinding signal](class.SignalAction.html). + */ + combo_box_signals[MOVE_ACTIVE] = + g_signal_new_class_handler (I_("move-active"), + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_CALLBACK (gtk_combo_box_real_move_active), + NULL, NULL, + NULL, + G_TYPE_NONE, 1, + GTK_TYPE_SCROLL_TYPE); + + /** + * GtkComboBox::popup: + * @widget: the object that received the signal + * + * Emitted to popup the combo box list. + * + * This is an [keybinding signal](class.SignalAction.html). + * + * The default binding for this signal is Alt+Down. + */ + combo_box_signals[POPUP] = + g_signal_new_class_handler (I_("popup"), + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_CALLBACK (gtk_combo_box_real_popup), + NULL, NULL, + NULL, + G_TYPE_NONE, 0); + /** + * GtkComboBox::popdown: + * @button: the object which received the signal + * + * Emitted to popdown the combo box list. + * + * This is an [keybinding signal](class.SignalAction.html). + * + * The default bindings for this signal are Alt+Up and Escape. + */ + combo_box_signals[POPDOWN] = + g_signal_new_class_handler (I_("popdown"), + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_CALLBACK (gtk_combo_box_real_popdown), + NULL, NULL, + _gtk_marshal_BOOLEAN__VOID, + G_TYPE_BOOLEAN, 0); + + /** + * GtkComboBox::format-entry-text: + * @combo: the object which received the signal + * @path: the [struct@Gtk.TreePath] string from the combo box's current model + * to format text for + * + * Emitted to allow changing how the text in a combo box's entry is displayed. + * + * See [property@Gtk.ComboBox:has-entry]. + * + * Connect a signal handler which returns an allocated string representing + * @path. That string will then be used to set the text in the combo box's + * entry. The default signal handler uses the text from the + * [property@Gtk.ComboBox:entry-text-column] model column. + * + * Here's an example signal handler which fetches data from the model and + * displays it in the entry. + * ```c + * static char * + * format_entry_text_callback (GtkComboBox *combo, + * const char *path, + * gpointer user_data) + * { + * GtkTreeIter iter; + * GtkTreeModel model; + * double value; + * + * model = gtk_combo_box_get_model (combo); + * + * gtk_tree_model_get_iter_from_string (model, &iter, path); + * gtk_tree_model_get (model, &iter, + * THE_DOUBLE_VALUE_COLUMN, &value, + * -1); + * + * return g_strdup_printf ("%g", value); + * } + * ``` + * + * Returns: (transfer full): a newly allocated string representing @path + * for the current `GtkComboBox` model. + */ + combo_box_signals[FORMAT_ENTRY_TEXT] = + g_signal_new (I_("format-entry-text"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkComboBoxClass, format_entry_text), + _gtk_single_string_accumulator, NULL, + _gtk_marshal_STRING__STRING, + G_TYPE_STRING, 1, G_TYPE_STRING); + + /* key bindings */ + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_Down, GDK_ALT_MASK, + "popup", + NULL); + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_KP_Down, GDK_ALT_MASK, + "popup", + NULL); + + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_Up, GDK_ALT_MASK, + "popdown", + NULL); + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_KP_Up, GDK_ALT_MASK, + "popdown", + NULL); + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_Escape, 0, + "popdown", + NULL); + + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_Up, 0, + "move-active", + "(i)", GTK_SCROLL_STEP_UP); + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_KP_Up, 0, + "move-active", + "(i)", GTK_SCROLL_STEP_UP); + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_Page_Up, 0, + "move-active", + "(i)", GTK_SCROLL_PAGE_UP); + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_KP_Page_Up, 0, + "move-active", + "(i)", GTK_SCROLL_PAGE_UP); + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_Home, 0, + "move-active", + "(i)", GTK_SCROLL_START); + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_KP_Home, 0, + "move-active", + "(i)", GTK_SCROLL_START); + + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_Down, 0, + "move-active", + "(i)", GTK_SCROLL_STEP_DOWN); + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_KP_Down, 0, + "move-active", + "(i)", GTK_SCROLL_STEP_DOWN); + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_Page_Down, 0, + "move-active", + "(i)", GTK_SCROLL_PAGE_DOWN); + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_KP_Page_Down, 0, + "move-active", + "(i)", GTK_SCROLL_PAGE_DOWN); + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_End, 0, + "move-active", + "(i)", GTK_SCROLL_END); + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_KP_End, 0, + "move-active", + "(i)", GTK_SCROLL_END); + + /* properties */ + g_object_class_override_property (object_class, + PROP_EDITING_CANCELED, + "editing-canceled"); + + /** + * GtkComboBox:model: (attributes org.gtk.Property.get=gtk_combo_box_get_model org.gtk.Property.set=gtk_combo_box_set_model) + * + * The model from which the combo box takes its values. + */ + g_object_class_install_property (object_class, + PROP_MODEL, + g_param_spec_object ("model", NULL, NULL, + GTK_TYPE_TREE_MODEL, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + + /** + * GtkComboBox:active: (attributes org.gtk.Property.get=gtk_combo_box_get_active org.gtk.Property.set=gtk_combo_box_set_active) + * + * The item which is currently active. + * + * If the model is a non-flat treemodel, and the active item is not an + * immediate child of the root of the tree, this property has the value + * `gtk_tree_path_get_indices (path)[0]`, where `path` is the + * [struct@Gtk.TreePath] of the active item. + */ + g_object_class_install_property (object_class, + PROP_ACTIVE, + g_param_spec_int ("active", NULL, NULL, + -1, + G_MAXINT, + -1, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + /** + * GtkComboBox:has-frame: + * + * The `has-frame` property controls whether a frame is drawn around the entry. + */ + g_object_class_install_property (object_class, + PROP_HAS_FRAME, + g_param_spec_boolean ("has-frame", NULL, NULL, + TRUE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + /** + * GtkComboBox:popup-shown: + * + * Whether the combo boxes dropdown is popped up. + * + * Note that this property is mainly useful, because + * it allows you to connect to notify::popup-shown. + */ + g_object_class_install_property (object_class, + PROP_POPUP_SHOWN, + g_param_spec_boolean ("popup-shown", NULL, NULL, + FALSE, + GTK_PARAM_READABLE)); + + + /** + * GtkComboBox:button-sensitivity: (attributes org.gtk.Property.get=gtk_combo_box_get_button_sensitivity org.gtk.Property.set=gtk_combo_box_set_button_sensitivity) + * + * Whether the dropdown button is sensitive when + * the model is empty. + */ + g_object_class_install_property (object_class, + PROP_BUTTON_SENSITIVITY, + g_param_spec_enum ("button-sensitivity", NULL, NULL, + GTK_TYPE_SENSITIVITY_TYPE, + GTK_SENSITIVITY_AUTO, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + /** + * GtkComboBox:has-entry: (attributes org.gtk.Property.get=gtk_combo_box_get_has_entry) + * + * Whether the combo box has an entry. + */ + g_object_class_install_property (object_class, + PROP_HAS_ENTRY, + g_param_spec_boolean ("has-entry", NULL, NULL, + FALSE, + GTK_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY)); + + /** + * GtkComboBox:entry-text-column: (attributes org.gtk.Property.get=gtk_combo_box_get_entry_text_column org.gtk.Property.set=gtk_combo_box_set_entry_text_column) + * + * The model column to associate with strings from the entry. + * + * This is property only relevant if the combo was created with + * [property@Gtk.ComboBox:has-entry] is %TRUE. + */ + g_object_class_install_property (object_class, + PROP_ENTRY_TEXT_COLUMN, + g_param_spec_int ("entry-text-column", NULL, NULL, + -1, G_MAXINT, -1, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + /** + * GtkComboBox:id-column: (attributes org.gtk.Property.get=gtk_combo_box_get_id_column org.gtk.Property.set=gtk_combo_box_set_id_column) + * + * The model column that provides string IDs for the values + * in the model, if != -1. + */ + g_object_class_install_property (object_class, + PROP_ID_COLUMN, + g_param_spec_int ("id-column", NULL, NULL, + -1, G_MAXINT, -1, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + /** + * GtkComboBox:active-id: (attributes org.gtk.Property.get=gtk_combo_box_get_active_id org.gtk.Property.set=gtk_combo_box_set_active_id) + * + * The value of the ID column of the active row. + */ + g_object_class_install_property (object_class, + PROP_ACTIVE_ID, + g_param_spec_string ("active-id", NULL, NULL, + NULL, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + /** + * GtkComboBox:popup-fixed-width: (attributes org.gtk.Property.get=gtk_combo_box_get_popup_fixed_width org.gtk.Property.set=gtk_combo_box_set_popup_fixed_width) + * + * Whether the popup's width should be a fixed width matching the + * allocated width of the combo box. + */ + g_object_class_install_property (object_class, + PROP_POPUP_FIXED_WIDTH, + g_param_spec_boolean ("popup-fixed-width", NULL, NULL, + TRUE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + /** + * GtkComboBox:child: (attributes org.gtk.Property.get=gtk_combo_box_get_child org.gtk.Property.set=gtk_combo_box_set_child) + * + * The child widget. + */ + g_object_class_install_property (object_class, + PROP_CHILD, + g_param_spec_object ("child", NULL, NULL, + GTK_TYPE_WIDGET, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/ui/gtkcombobox.ui"); + gtk_widget_class_bind_template_child_internal_private (widget_class, GtkComboBox, box); + gtk_widget_class_bind_template_child_internal_private (widget_class, GtkComboBox, button); + gtk_widget_class_bind_template_child_internal_private (widget_class, GtkComboBox, arrow); + gtk_widget_class_bind_template_child_internal_private (widget_class, GtkComboBox, area); + gtk_widget_class_bind_template_child_internal_private (widget_class, GtkComboBox, popup_widget); + gtk_widget_class_bind_template_callback (widget_class, gtk_combo_box_button_toggled); + gtk_widget_class_bind_template_callback (widget_class, gtk_combo_box_menu_activate); + gtk_widget_class_bind_template_callback (widget_class, gtk_combo_box_menu_key); + gtk_widget_class_bind_template_callback (widget_class, gtk_combo_box_menu_show); + gtk_widget_class_bind_template_callback (widget_class, gtk_combo_box_menu_hide); + + gtk_widget_class_set_css_name (widget_class, I_("combobox")); + gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_COMBO_BOX); +} + +static void +gtk_combo_box_buildable_init (GtkBuildableIface *iface) +{ + parent_buildable_iface = g_type_interface_peek_parent (iface); + iface->add_child = gtk_combo_box_buildable_add_child; + iface->custom_tag_start = gtk_combo_box_buildable_custom_tag_start; + iface->custom_tag_end = gtk_combo_box_buildable_custom_tag_end; + iface->get_internal_child = gtk_combo_box_buildable_get_internal_child; +} + +static void +gtk_combo_box_cell_layout_init (GtkCellLayoutIface *iface) +{ + iface->get_area = gtk_combo_box_cell_layout_get_area; +} + +static void +gtk_combo_box_cell_editable_init (GtkCellEditableIface *iface) +{ + iface->start_editing = gtk_combo_box_start_editing; +} + +static gboolean +gtk_combo_box_row_separator_func (GtkTreeModel *model, + GtkTreeIter *iter, + GtkComboBox *combo_box) +{ + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + + if (priv->row_separator_func) + return priv->row_separator_func (model, iter, priv->row_separator_data); + + return FALSE; +} + +static void +gtk_combo_box_init (GtkComboBox *combo_box) +{ + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + GtkEventController *controller; + GtkEventController **controllers; + guint n_controllers, i; + + priv->active = -1; + priv->active_row = NULL; + + priv->popup_shown = FALSE; + priv->has_frame = TRUE; + priv->is_cell_renderer = FALSE; + priv->editing_canceled = FALSE; + priv->auto_scroll = FALSE; + priv->button_sensitivity = GTK_SENSITIVITY_AUTO; + priv->has_entry = FALSE; + priv->popup_fixed_width = TRUE; + + priv->text_column = -1; + priv->text_renderer = NULL; + priv->id_column = -1; + + g_type_ensure (GTK_TYPE_BUILTIN_ICON); + g_type_ensure (GTK_TYPE_TREE_POPOVER); + gtk_widget_init_template (GTK_WIDGET (combo_box)); + + gtk_widget_remove_css_class (priv->button, "toggle"); + gtk_widget_add_css_class (priv->button, "combo"); + + gtk_tree_popover_set_row_separator_func (GTK_TREE_POPOVER (priv->popup_widget), + (GtkTreeViewRowSeparatorFunc)gtk_combo_box_row_separator_func, + combo_box, NULL); + + controller = gtk_event_controller_scroll_new (GTK_EVENT_CONTROLLER_SCROLL_VERTICAL | + GTK_EVENT_CONTROLLER_SCROLL_DISCRETE); + g_signal_connect (controller, "scroll", + G_CALLBACK (gtk_combo_box_scroll_controller_scroll), + combo_box); + gtk_widget_add_controller (GTK_WIDGET (combo_box), controller); + + controllers = gtk_widget_list_controllers (priv->popup_widget, GTK_PHASE_BUBBLE, &n_controllers); + for (i = 0; i < n_controllers; i ++) + { + controller = controllers[i]; + + if (GTK_IS_SHORTCUT_CONTROLLER (controller)) + { + g_object_ref (controller); + gtk_widget_remove_controller (priv->popup_widget, controller); + gtk_widget_add_controller (priv->popup_widget, controller); + break; + } + } + g_free (controllers); +} + +static void +gtk_combo_box_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkComboBox *combo_box = GTK_COMBO_BOX (object); + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + + switch (prop_id) + { + case PROP_MODEL: + gtk_combo_box_set_model (combo_box, g_value_get_object (value)); + break; + + case PROP_ACTIVE: + gtk_combo_box_set_active (combo_box, g_value_get_int (value)); + break; + + case PROP_HAS_FRAME: + if (priv->has_frame != g_value_get_boolean (value)) + { + priv->has_frame = g_value_get_boolean (value); + if (priv->has_entry) + gtk_entry_set_has_frame (GTK_ENTRY (priv->child), priv->has_frame); + g_object_notify (object, "has-frame"); + } + break; + + case PROP_POPUP_SHOWN: + if (g_value_get_boolean (value)) + gtk_combo_box_popup (combo_box); + else + gtk_combo_box_popdown (combo_box); + break; + + case PROP_BUTTON_SENSITIVITY: + gtk_combo_box_set_button_sensitivity (combo_box, + g_value_get_enum (value)); + break; + + case PROP_POPUP_FIXED_WIDTH: + gtk_combo_box_set_popup_fixed_width (combo_box, + g_value_get_boolean (value)); + break; + + case PROP_EDITING_CANCELED: + if (priv->editing_canceled != g_value_get_boolean (value)) + { + priv->editing_canceled = g_value_get_boolean (value); + g_object_notify (object, "editing-canceled"); + } + break; + + case PROP_HAS_ENTRY: + priv->has_entry = g_value_get_boolean (value); + break; + + case PROP_ENTRY_TEXT_COLUMN: + gtk_combo_box_set_entry_text_column (combo_box, g_value_get_int (value)); + break; + + case PROP_ID_COLUMN: + gtk_combo_box_set_id_column (combo_box, g_value_get_int (value)); + break; + + case PROP_ACTIVE_ID: + gtk_combo_box_set_active_id (combo_box, g_value_get_string (value)); + break; + + case PROP_CHILD: + gtk_combo_box_set_child (combo_box, g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_combo_box_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkComboBox *combo_box = GTK_COMBO_BOX (object); + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + + switch (prop_id) + { + case PROP_MODEL: + g_value_set_object (value, priv->model); + break; + + case PROP_ACTIVE: + g_value_set_int (value, gtk_combo_box_get_active (combo_box)); + break; + + case PROP_HAS_FRAME: + g_value_set_boolean (value, priv->has_frame); + break; + + case PROP_POPUP_SHOWN: + g_value_set_boolean (value, priv->popup_shown); + break; + + case PROP_BUTTON_SENSITIVITY: + g_value_set_enum (value, priv->button_sensitivity); + break; + + case PROP_POPUP_FIXED_WIDTH: + g_value_set_boolean (value, priv->popup_fixed_width); + break; + + case PROP_EDITING_CANCELED: + g_value_set_boolean (value, priv->editing_canceled); + break; + + case PROP_HAS_ENTRY: + g_value_set_boolean (value, priv->has_entry); + break; + + case PROP_ENTRY_TEXT_COLUMN: + g_value_set_int (value, priv->text_column); + break; + + case PROP_ID_COLUMN: + g_value_set_int (value, priv->id_column); + break; + + case PROP_ACTIVE_ID: + g_value_set_string (value, gtk_combo_box_get_active_id (combo_box)); + break; + + case PROP_CHILD: + g_value_set_object (value, gtk_combo_box_get_child (combo_box)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_combo_box_button_toggled (GtkWidget *widget, + gpointer data) +{ + GtkComboBox *combo_box = GTK_COMBO_BOX (data); + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) + { + if (!priv->popup_in_progress) + gtk_combo_box_popup (combo_box); + } + else + { + gtk_combo_box_popdown (combo_box); + } +} + +static void +gtk_combo_box_create_child (GtkComboBox *combo_box) +{ + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + + if (priv->has_entry) + { + GtkWidget *entry; + + entry = gtk_entry_new (); + gtk_combo_box_set_child (combo_box, entry); + + gtk_widget_add_css_class (GTK_WIDGET (entry), "combo"); + + g_signal_connect (combo_box, "changed", + G_CALLBACK (gtk_combo_box_entry_active_changed), NULL); + } + else + { + priv->cell_view = gtk_cell_view_new_with_context (priv->area, NULL); + gtk_widget_set_hexpand (priv->cell_view, TRUE); + gtk_cell_view_set_fit_model (GTK_CELL_VIEW (priv->cell_view), TRUE); + gtk_cell_view_set_model (GTK_CELL_VIEW (priv->cell_view), priv->model); + gtk_box_insert_child_after (GTK_BOX (gtk_widget_get_parent (priv->arrow)), priv->cell_view, NULL); + priv->child = priv->cell_view; + } +} + +static void +gtk_combo_box_add (GtkComboBox *combo_box, + GtkWidget *widget) +{ + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + + if (priv->box == NULL) + { + gtk_widget_set_parent (widget, GTK_WIDGET (combo_box)); + return; + } + + if (priv->has_entry && !GTK_IS_ENTRY (widget)) + { + g_warning ("Attempting to add a widget with type %s to a GtkComboBox that needs an entry " + "(need an instance of GtkEntry or of a subclass)", + G_OBJECT_TYPE_NAME (widget)); + return; + } + + g_clear_pointer (&priv->cell_view, gtk_widget_unparent); + + gtk_widget_set_hexpand (widget, TRUE); + gtk_box_insert_child_after (GTK_BOX (priv->box), widget, NULL); + + priv->child = widget; + + if (priv->has_entry) + { + g_signal_connect (widget, "changed", + G_CALLBACK (gtk_combo_box_entry_contents_changed), + combo_box); + + gtk_entry_set_has_frame (GTK_ENTRY (widget), priv->has_frame); + } +} + +static void +gtk_combo_box_remove (GtkComboBox *combo_box, + GtkWidget *widget) +{ + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + GtkTreePath *path; + + if (priv->has_entry) + { + if (widget && widget == priv->child) + g_signal_handlers_disconnect_by_func (widget, + gtk_combo_box_entry_contents_changed, + combo_box); + } + + gtk_box_remove (GTK_BOX (priv->box), widget); + + priv->child = NULL; + + if (gtk_widget_in_destruction (GTK_WIDGET (combo_box))) + return; + + gtk_widget_queue_resize (GTK_WIDGET (combo_box)); + + gtk_combo_box_create_child (combo_box); + + if (gtk_tree_row_reference_valid (priv->active_row)) + { + path = gtk_tree_row_reference_get_path (priv->active_row); + gtk_combo_box_set_active_internal (combo_box, path); + gtk_tree_path_free (path); + } + else + { + gtk_combo_box_set_active_internal (combo_box, NULL); + } +} + +static void +gtk_combo_box_menu_show (GtkWidget *menu, + gpointer user_data) +{ + GtkComboBox *combo_box = GTK_COMBO_BOX (user_data); + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + + gtk_combo_box_child_show (menu, user_data); + + priv->popup_in_progress = TRUE; + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button), + TRUE); + priv->popup_in_progress = FALSE; +} + +static void +gtk_combo_box_menu_hide (GtkWidget *menu, + gpointer user_data) +{ + GtkComboBox *combo_box = GTK_COMBO_BOX (user_data); + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + + gtk_combo_box_child_hide (menu,user_data); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button), FALSE); +} + +static gboolean +cell_layout_is_sensitive (GtkCellLayout *layout) +{ + GList *cells, *list; + gboolean sensitive; + + cells = gtk_cell_layout_get_cells (layout); + + sensitive = FALSE; + for (list = cells; list; list = list->next) + { + g_object_get (list->data, "sensitive", &sensitive, NULL); + + if (sensitive) + break; + } + g_list_free (cells); + + return sensitive; +} + +static gboolean +tree_column_row_is_sensitive (GtkComboBox *combo_box, + GtkTreeIter *iter) +{ + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + + if (priv->row_separator_func) + { + if (priv->row_separator_func (priv->model, iter, + priv->row_separator_data)) + return FALSE; + } + + gtk_cell_area_apply_attributes (priv->area, priv->model, iter, FALSE, FALSE); + return cell_layout_is_sensitive (GTK_CELL_LAYOUT (priv->area)); +} + +static void +gtk_combo_box_menu_popup (GtkComboBox *combo_box) +{ + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); +#if 0 + int active_item; + GtkWidget *active; + int width, min_width, nat_width; +#endif + + gtk_tree_popover_open_submenu (GTK_TREE_POPOVER (priv->popup_widget), "main"); + gtk_popover_popup (GTK_POPOVER (priv->popup_widget)); + +#if 0 + active_item = -1; + if (gtk_tree_row_reference_valid (priv->active_row)) + { + GtkTreePath *path; + + path = gtk_tree_row_reference_get_path (priv->active_row); + active_item = gtk_tree_path_get_indices (path)[0]; + gtk_tree_path_free (path); + } + + /* FIXME handle nested menus better */ + //gtk_tree_popover_set_active (GTK_TREE_POPOVER (priv->popup_widget), active_item); + + width = gtk_widget_get_width (GTK_WIDGET (combo_box)); + gtk_widget_set_size_request (priv->popup_widget, -1, -1); + gtk_widget_measure (priv->popup_widget, GTK_ORIENTATION_HORIZONTAL, -1, + &min_width, &nat_width, NULL, NULL); + + if (priv->popup_fixed_width) + width = MAX (width, min_width); + else + width = MAX (width, nat_width); + + gtk_widget_set_size_request (priv->popup_widget, width, -1); + + g_signal_handlers_disconnect_by_func (priv->popup_widget, + gtk_menu_update_scroll_offset, + NULL); + + if (priv->cell_view == NULL) + { + g_object_set (priv->popup_widget, + "anchor-hints", (GDK_ANCHOR_FLIP_Y | + GDK_ANCHOR_SLIDE | + GDK_ANCHOR_RESIZE), + "rect-anchor-dx", 0, + NULL); + + gtk_menu_popup_at_widget (GTK_MENU (priv->popup_widget), + gtk_bin_get_child (GTK_BIN (combo_box)), + GDK_GRAVITY_SOUTH_WEST, + GDK_GRAVITY_NORTH_WEST, + NULL); + } + else + { + int rect_anchor_dy = -2; + GList *i; + GtkWidget *child; + + /* FIXME handle nested menus better */ + active = gtk_menu_get_active (GTK_MENU (priv->popup_widget)); + + if (!(active && gtk_widget_get_visible (active))) + { + GList *children; + children = gtk_menu_shell_get_items (GTK_MENU_SHELL (priv->popup_widget)); + for (i = children; i && !active; i = i->next) + { + child = i->data; + + if (child && gtk_widget_get_visible (child)) + active = child; + } + g_list_free (children); + } + + if (active) + { + int child_height; + GList *children; + children = gtk_menu_shell_get_items (GTK_MENU_SHELL (priv->popup_widget)); + for (i = children; i && i->data != active; i = i->next) + { + child = i->data; + + if (child && gtk_widget_get_visible (child)) + { + gtk_widget_measure (child, GTK_ORIENTATION_VERTICAL, -1, + &child_height, NULL, NULL, NULL); + rect_anchor_dy -= child_height; + } + } + g_list_free (children); + + gtk_widget_measure (active, GTK_ORIENTATION_VERTICAL, -1, + &child_height, NULL, NULL, NULL); + rect_anchor_dy -= child_height / 2; + } + + g_object_set (priv->popup_widget, + "anchor-hints", (GDK_ANCHOR_SLIDE | + GDK_ANCHOR_RESIZE), + "rect-anchor-dy", rect_anchor_dy, + NULL); + + g_signal_connect (priv->popup_widget, + "popped-up", + G_CALLBACK (gtk_menu_update_scroll_offset), + NULL); + + gtk_menu_popup_at_widget (GTK_MENU (priv->popup_widget), + GTK_WIDGET (combo_box), + GDK_GRAVITY_WEST, + GDK_GRAVITY_NORTH_WEST, + NULL); + } +#endif +} + +/** + * gtk_combo_box_popup: + * @combo_box: a `GtkComboBox` + * + * Pops up the menu or dropdown list of @combo_box. + * + * This function is mostly intended for use by accessibility technologies; + * applications should have little use for it. + * + * Before calling this, @combo_box must be mapped, or nothing will happen. + * + * Deprecated: 4.10: Use GtkDropDown + */ +void +gtk_combo_box_popup (GtkComboBox *combo_box) +{ + g_return_if_fail (GTK_IS_COMBO_BOX (combo_box)); + + if (gtk_widget_get_mapped (GTK_WIDGET (combo_box))) + g_signal_emit (combo_box, combo_box_signals[POPUP], 0); +} + +/** + * gtk_combo_box_popup_for_device: + * @combo_box: a `GtkComboBox` + * @device: a `GdkDevice` + * + * Pops up the menu of @combo_box. + * + * Note that currently this does not do anything with the device, as it was + * previously only used for list-mode combo boxes, and those were removed + * in GTK 4. However, it is retained in case similar functionality is added + * back later. + * + * Deprecated: 4.10: Use GtkDropDown + */ +void +gtk_combo_box_popup_for_device (GtkComboBox *combo_box, + GdkDevice *device) +{ + /* As above, this currently does not do anything useful, and nothing with the + * passed-in device. But the bits that are not blatantly obsolete are kept. */ + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + + g_return_if_fail (GTK_IS_COMBO_BOX (combo_box)); + g_return_if_fail (GDK_IS_DEVICE (device)); + + if (!gtk_widget_get_realized (GTK_WIDGET (combo_box))) + return; + + if (gtk_widget_get_mapped (priv->popup_widget)) + return; + + gtk_combo_box_menu_popup (combo_box); +} + +static void +gtk_combo_box_real_popup (GtkComboBox *combo_box) +{ + gtk_combo_box_menu_popup (combo_box); +} + +static gboolean +gtk_combo_box_real_popdown (GtkComboBox *combo_box) +{ + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + + if (priv->popup_shown) + { + gtk_combo_box_popdown (combo_box); + return TRUE; + } + + return FALSE; +} + +/** + * gtk_combo_box_popdown: + * @combo_box: a `GtkComboBox` + * + * Hides the menu or dropdown list of @combo_box. + * + * This function is mostly intended for use by accessibility technologies; + * applications should have little use for it. + * + * Deprecated: 4.10: Use GtkDropDown + */ +void +gtk_combo_box_popdown (GtkComboBox *combo_box) +{ + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + + g_return_if_fail (GTK_IS_COMBO_BOX (combo_box)); + + gtk_popover_popdown (GTK_POPOVER (priv->popup_widget)); +} + +static void +gtk_combo_box_unset_model (GtkComboBox *combo_box) +{ + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + + if (priv->model) + { + g_signal_handlers_disconnect_by_func (priv->model, + gtk_combo_box_model_row_inserted, + combo_box); + g_signal_handlers_disconnect_by_func (priv->model, + gtk_combo_box_model_row_deleted, + combo_box); + g_signal_handlers_disconnect_by_func (priv->model, + gtk_combo_box_model_rows_reordered, + combo_box); + g_signal_handlers_disconnect_by_func (priv->model, + gtk_combo_box_model_row_changed, + combo_box); + + g_object_unref (priv->model); + priv->model = NULL; + } + + if (priv->active_row) + { + gtk_tree_row_reference_free (priv->active_row); + priv->active_row = NULL; + } + + if (priv->cell_view) + gtk_cell_view_set_model (GTK_CELL_VIEW (priv->cell_view), NULL); +} + +static void +gtk_combo_box_child_show (GtkWidget *widget, + GtkComboBox *combo_box) +{ + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + + priv->popup_shown = TRUE; + g_object_notify (G_OBJECT (combo_box), "popup-shown"); +} + +static void +gtk_combo_box_child_hide (GtkWidget *widget, + GtkComboBox *combo_box) +{ + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + + priv->popup_shown = FALSE; + g_object_notify (G_OBJECT (combo_box), "popup-shown"); +} + +typedef struct { + GtkComboBox *combo; + GtkTreePath *path; + GtkTreeIter iter; + gboolean found; + gboolean set; +} SearchData; + +static gboolean +tree_next_func (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + SearchData *search_data = (SearchData *)data; + + if (search_data->found) + { + if (!tree_column_row_is_sensitive (search_data->combo, iter)) + return FALSE; + + search_data->set = TRUE; + search_data->iter = *iter; + + return TRUE; + } + + if (gtk_tree_path_compare (path, search_data->path) == 0) + search_data->found = TRUE; + + return FALSE; +} + +static gboolean +tree_next (GtkComboBox *combo, + GtkTreeModel *model, + GtkTreeIter *iter, + GtkTreeIter *next) +{ + SearchData search_data; + + search_data.combo = combo; + search_data.path = gtk_tree_model_get_path (model, iter); + search_data.found = FALSE; + search_data.set = FALSE; + + gtk_tree_model_foreach (model, tree_next_func, &search_data); + + *next = search_data.iter; + + gtk_tree_path_free (search_data.path); + + return search_data.set; +} + +static gboolean +tree_prev_func (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + SearchData *search_data = (SearchData *)data; + + if (gtk_tree_path_compare (path, search_data->path) == 0) + { + search_data->found = TRUE; + return TRUE; + } + + if (!tree_column_row_is_sensitive (search_data->combo, iter)) + return FALSE; + + search_data->set = TRUE; + search_data->iter = *iter; + + return FALSE; +} + +static gboolean +tree_prev (GtkComboBox *combo, + GtkTreeModel *model, + GtkTreeIter *iter, + GtkTreeIter *prev) +{ + SearchData search_data; + + search_data.combo = combo; + search_data.path = gtk_tree_model_get_path (model, iter); + search_data.found = FALSE; + search_data.set = FALSE; + + gtk_tree_model_foreach (model, tree_prev_func, &search_data); + + *prev = search_data.iter; + + gtk_tree_path_free (search_data.path); + + return search_data.set; +} + +static gboolean +tree_last_func (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + SearchData *search_data = (SearchData *)data; + + if (!tree_column_row_is_sensitive (search_data->combo, iter)) + return FALSE; + + search_data->set = TRUE; + search_data->iter = *iter; + + return FALSE; +} + +static gboolean +tree_last (GtkComboBox *combo, + GtkTreeModel *model, + GtkTreeIter *last) +{ + SearchData search_data; + + search_data.combo = combo; + search_data.set = FALSE; + + gtk_tree_model_foreach (model, tree_last_func, &search_data); + + *last = search_data.iter; + + return search_data.set; +} + + +static gboolean +tree_first_func (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + SearchData *search_data = (SearchData *)data; + + if (!tree_column_row_is_sensitive (search_data->combo, iter)) + return FALSE; + + search_data->set = TRUE; + search_data->iter = *iter; + + return TRUE; +} + +static gboolean +tree_first (GtkComboBox *combo, + GtkTreeModel *model, + GtkTreeIter *first) +{ + SearchData search_data; + + search_data.combo = combo; + search_data.set = FALSE; + + gtk_tree_model_foreach (model, tree_first_func, &search_data); + + *first = search_data.iter; + + return search_data.set; +} + +static gboolean +gtk_combo_box_scroll_controller_scroll (GtkEventControllerScroll *scroll, + double dx, + double dy, + GtkComboBox *combo_box) +{ + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + gboolean found = FALSE; + GtkTreeIter iter; + GtkTreeIter new_iter; + + if (!gtk_combo_box_get_active_iter (combo_box, &iter)) + return GDK_EVENT_PROPAGATE; + + if (dy < 0) + found = tree_prev (combo_box, priv->model, &iter, &new_iter); + else if (dy > 0) + found = tree_next (combo_box, priv->model, &iter, &new_iter); + + if (found) + gtk_combo_box_set_active_iter (combo_box, &new_iter); + + return found; + +} + +/* callbacks */ +static void +gtk_combo_box_menu_activate (GtkWidget *menu, + const char *path, + GtkComboBox *combo_box) +{ + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + GtkTreeIter iter; + + if (gtk_tree_model_get_iter_from_string (priv->model, &iter, path)) + gtk_combo_box_set_active_iter (combo_box, &iter); + + g_object_set (combo_box, + "editing-canceled", FALSE, + NULL); +} + + +static void +gtk_combo_box_update_sensitivity (GtkComboBox *combo_box) +{ + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + GtkTreeIter iter; + gboolean sensitive = TRUE; /* fool code checkers */ + + if (!priv->button) + return; + + switch (priv->button_sensitivity) + { + case GTK_SENSITIVITY_ON: + sensitive = TRUE; + break; + case GTK_SENSITIVITY_OFF: + sensitive = FALSE; + break; + case GTK_SENSITIVITY_AUTO: + sensitive = priv->model && + gtk_tree_model_get_iter_first (priv->model, &iter); + break; + default: + g_assert_not_reached (); + break; + } + + gtk_widget_set_sensitive (priv->button, sensitive); +} + +static void +gtk_combo_box_model_row_inserted (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer user_data) +{ + GtkComboBox *combo_box = GTK_COMBO_BOX (user_data); + + gtk_combo_box_update_sensitivity (combo_box); +} + +static void +gtk_combo_box_model_row_deleted (GtkTreeModel *model, + GtkTreePath *path, + gpointer user_data) +{ + GtkComboBox *combo_box = GTK_COMBO_BOX (user_data); + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + + if (!gtk_tree_row_reference_valid (priv->active_row)) + { + if (priv->cell_view) + gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (priv->cell_view), NULL); + g_signal_emit (combo_box, combo_box_signals[CHANGED], 0); + } + + gtk_combo_box_update_sensitivity (combo_box); +} + +static void +gtk_combo_box_model_rows_reordered (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + int *new_order, + gpointer user_data) +{ + gtk_tree_row_reference_reordered (G_OBJECT (user_data), path, iter, new_order); +} + +static void +gtk_combo_box_model_row_changed (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer user_data) +{ + GtkComboBox *combo_box = GTK_COMBO_BOX (user_data); + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + GtkTreePath *active_path; + + /* FIXME this belongs to GtkCellView */ + if (gtk_tree_row_reference_valid (priv->active_row)) + { + active_path = gtk_tree_row_reference_get_path (priv->active_row); + if (gtk_tree_path_compare (path, active_path) == 0 && + priv->cell_view) + gtk_widget_queue_resize (GTK_WIDGET (priv->cell_view)); + gtk_tree_path_free (active_path); + } +} + +static gboolean +gtk_combo_box_menu_key (GtkEventControllerKey *key, + guint keyval, + guint keycode, + GdkModifierType modifiers, + GtkComboBox *combo_box) +{ + gtk_event_controller_key_forward (key, GTK_WIDGET (combo_box)); + + return TRUE; +} + +/* + * GtkCellLayout implementation + */ +static GtkCellArea * +gtk_combo_box_cell_layout_get_area (GtkCellLayout *cell_layout) +{ + GtkComboBox *combo_box = GTK_COMBO_BOX (cell_layout); + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + + return priv->area; +} + +/* + * public API + */ + +/** + * gtk_combo_box_new: + * + * Creates a new empty `GtkComboBox`. + * + * Returns: A new `GtkComboBox` + * + * Deprecated: 4.10: Use GtkDropDown + */ +GtkWidget * +gtk_combo_box_new (void) +{ + return g_object_new (GTK_TYPE_COMBO_BOX, NULL); +} + +/** + * gtk_combo_box_new_with_entry: + * + * Creates a new empty `GtkComboBox` with an entry. + * + * In order to use a combo box with entry, you need to tell it + * which column of the model contains the text for the entry + * by calling [method@Gtk.ComboBox.set_entry_text_column]. + * + * Returns: A new `GtkComboBox` + * + * Deprecated: 4.10: Use GtkDropDown + */ +GtkWidget * +gtk_combo_box_new_with_entry (void) +{ + return g_object_new (GTK_TYPE_COMBO_BOX, "has-entry", TRUE, NULL); +} + +/** + * gtk_combo_box_new_with_model: + * @model: a `GtkTreeModel` + * + * Creates a new `GtkComboBox` with a model. + * + * Returns: A new `GtkComboBox` + * + * Deprecated: 4.10: Use GtkDropDown + */ +GtkWidget * +gtk_combo_box_new_with_model (GtkTreeModel *model) +{ + GtkComboBox *combo_box; + + g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL); + + combo_box = g_object_new (GTK_TYPE_COMBO_BOX, "model", model, NULL); + + return GTK_WIDGET (combo_box); +} + +/** + * gtk_combo_box_new_with_model_and_entry: + * @model: A `GtkTreeModel` + * + * Creates a new empty `GtkComboBox` with an entry and a model. + * + * See also [ctor@Gtk.ComboBox.new_with_entry]. + * + * Returns: A new `GtkComboBox` + * + * Deprecated: 4.10: Use GtkDropDown + */ +GtkWidget * +gtk_combo_box_new_with_model_and_entry (GtkTreeModel *model) +{ + return g_object_new (GTK_TYPE_COMBO_BOX, + "has-entry", TRUE, + "model", model, + NULL); +} + +/** + * gtk_combo_box_get_active: + * @combo_box: A `GtkComboBox` + * + * Returns the index of the currently active item. + * + * If the model is a non-flat treemodel, and the active item is not + * an immediate child of the root of the tree, this function returns + * `gtk_tree_path_get_indices (path)[0]`, where `path` is the + * [struct@Gtk.TreePath] of the active item. + * + * Returns: An integer which is the index of the currently active item, + * or -1 if there’s no active item + * + * Deprecated: 4.10: Use GtkDropDown + */ +int +gtk_combo_box_get_active (GtkComboBox *combo_box) +{ + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + int result; + + g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), 0); + + if (gtk_tree_row_reference_valid (priv->active_row)) + { + GtkTreePath *path; + + path = gtk_tree_row_reference_get_path (priv->active_row); + result = gtk_tree_path_get_indices (path)[0]; + gtk_tree_path_free (path); + } + else + result = -1; + + return result; +} + +/** + * gtk_combo_box_set_active: (attributes org.gtk.Method.set_property=active) + * @combo_box: a `GtkComboBox` + * @index_: An index in the model passed during construction, + * or -1 to have no active item + * + * Sets the active item of @combo_box to be the item at @index. + * + * Deprecated: 4.10: Use GtkDropDown + */ +void +gtk_combo_box_set_active (GtkComboBox *combo_box, + int index_) +{ + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + GtkTreePath *path = NULL; + + g_return_if_fail (GTK_IS_COMBO_BOX (combo_box)); + g_return_if_fail (index_ >= -1); + + if (priv->model == NULL) + { + /* Save index, in case the model is set after the index */ + priv->active = index_; + if (index_ != -1) + return; + } + + if (index_ != -1) + path = gtk_tree_path_new_from_indices (index_, -1); + + gtk_combo_box_set_active_internal (combo_box, path); + + if (path) + gtk_tree_path_free (path); +} + +static void +gtk_combo_box_set_active_internal (GtkComboBox *combo_box, + GtkTreePath *path) +{ + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + GtkTreePath *active_path; + int path_cmp; + + /* Remember whether the initially active row is valid. */ + gboolean is_valid_row_reference = gtk_tree_row_reference_valid (priv->active_row); + + if (path && is_valid_row_reference) + { + active_path = gtk_tree_row_reference_get_path (priv->active_row); + path_cmp = gtk_tree_path_compare (path, active_path); + gtk_tree_path_free (active_path); + if (path_cmp == 0) + return; + } + + if (priv->active_row) + { + gtk_tree_row_reference_free (priv->active_row); + priv->active_row = NULL; + } + + if (!path) + { + gtk_tree_popover_set_active (GTK_TREE_POPOVER (priv->popup_widget), -1); + + if (priv->cell_view) + gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (priv->cell_view), NULL); + + /* + * Do not emit a "changed" signal when an already invalid selection was + * now set to invalid. + */ + if (!is_valid_row_reference) + return; + } + else + { + priv->active_row = + gtk_tree_row_reference_new (priv->model, path); + + gtk_tree_popover_set_active (GTK_TREE_POPOVER (priv->popup_widget), + gtk_tree_path_get_indices (path)[0]); + + if (priv->cell_view) + gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (priv->cell_view), path); + } + + g_signal_emit (combo_box, combo_box_signals[CHANGED], 0); + g_object_notify (G_OBJECT (combo_box), "active"); + if (priv->id_column >= 0) + g_object_notify (G_OBJECT (combo_box), "active-id"); +} + + +/** + * gtk_combo_box_get_active_iter: + * @combo_box: A `GtkComboBox` + * @iter: (out): A `GtkTreeIter` + * + * Sets @iter to point to the currently active item. + * + * If no item is active, @iter is left unchanged. + * + * Returns: %TRUE if @iter was set, %FALSE otherwise + * + * Deprecated: 4.10: Use GtkDropDown + */ +gboolean +gtk_combo_box_get_active_iter (GtkComboBox *combo_box, + GtkTreeIter *iter) +{ + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + GtkTreePath *path; + gboolean result; + + g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE); + + if (!gtk_tree_row_reference_valid (priv->active_row)) + return FALSE; + + path = gtk_tree_row_reference_get_path (priv->active_row); + result = gtk_tree_model_get_iter (priv->model, iter, path); + gtk_tree_path_free (path); + + return result; +} + +/** + * gtk_combo_box_set_active_iter: + * @combo_box: A `GtkComboBox` + * @iter: (nullable): The `GtkTreeIter` + * + * Sets the current active item to be the one referenced by @iter. + * + * If @iter is %NULL, the active item is unset. + * + * Deprecated: 4.10: Use GtkDropDown + */ +void +gtk_combo_box_set_active_iter (GtkComboBox *combo_box, + GtkTreeIter *iter) +{ + GtkTreePath *path = NULL; + + g_return_if_fail (GTK_IS_COMBO_BOX (combo_box)); + + if (iter) + path = gtk_tree_model_get_path (gtk_combo_box_get_model (combo_box), iter); + + gtk_combo_box_set_active_internal (combo_box, path); + gtk_tree_path_free (path); +} + +/** + * gtk_combo_box_set_model: (attributes org.gtk.Method.set_property=model) + * @combo_box: A `GtkComboBox` + * @model: (nullable): A `GtkTreeModel` + * + * Sets the model used by @combo_box to be @model. + * + * Will unset a previously set model (if applicable). If model is %NULL, + * then it will unset the model. + * + * Note that this function does not clear the cell renderers, you have to + * call [method@Gtk.CellLayout.clear] yourself if you need to set up different + * cell renderers for the new model. + * + * Deprecated: 4.10: Use GtkDropDown + */ +void +gtk_combo_box_set_model (GtkComboBox *combo_box, + GtkTreeModel *model) +{ + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + + g_return_if_fail (GTK_IS_COMBO_BOX (combo_box)); + g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model)); + + if (model == priv->model) + return; + + gtk_combo_box_unset_model (combo_box); + + if (model == NULL) + goto out; + + priv->model = model; + g_object_ref (priv->model); + + g_signal_connect (priv->model, "row-inserted", + G_CALLBACK (gtk_combo_box_model_row_inserted), + combo_box); + g_signal_connect (priv->model, "row-deleted", + G_CALLBACK (gtk_combo_box_model_row_deleted), + combo_box); + g_signal_connect (priv->model, "rows-reordered", + G_CALLBACK (gtk_combo_box_model_rows_reordered), + combo_box); + g_signal_connect (priv->model, "row-changed", + G_CALLBACK (gtk_combo_box_model_row_changed), + combo_box); + + gtk_tree_popover_set_model (GTK_TREE_POPOVER (priv->popup_widget), priv->model); + + if (priv->cell_view) + gtk_cell_view_set_model (GTK_CELL_VIEW (priv->cell_view), + priv->model); + + if (priv->active != -1) + { + /* If an index was set in advance, apply it now */ + gtk_combo_box_set_active (combo_box, priv->active); + priv->active = -1; + } + +out: + gtk_combo_box_update_sensitivity (combo_box); + + g_object_notify (G_OBJECT (combo_box), "model"); +} + +/** + * gtk_combo_box_get_model: (attributes org.gtk.Method.get_property=model) + * @combo_box: A `GtkComboBox` + * + * Returns the `GtkTreeModel` of @combo_box. + * + * Returns: (nullable) (transfer none): A `GtkTreeModel` which was passed + * during construction. + * + * Deprecated: 4.10: Use GtkDropDown + */ +GtkTreeModel * +gtk_combo_box_get_model (GtkComboBox *combo_box) +{ + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + + g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL); + + return priv->model; +} + +static void +gtk_combo_box_real_move_active (GtkComboBox *combo_box, + GtkScrollType scroll) +{ + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + GtkTreeIter iter; + GtkTreeIter new_iter; + gboolean active_iter; + gboolean found; + + if (!priv->model) + { + gtk_widget_error_bell (GTK_WIDGET (combo_box)); + return; + } + + active_iter = gtk_combo_box_get_active_iter (combo_box, &iter); + + switch (scroll) + { + case GTK_SCROLL_STEP_BACKWARD: + case GTK_SCROLL_STEP_UP: + case GTK_SCROLL_STEP_LEFT: + if (active_iter) + { + found = tree_prev (combo_box, priv->model, + &iter, &new_iter); + break; + } + G_GNUC_FALLTHROUGH; + + case GTK_SCROLL_PAGE_FORWARD: + case GTK_SCROLL_PAGE_DOWN: + case GTK_SCROLL_PAGE_RIGHT: + case GTK_SCROLL_END: + found = tree_last (combo_box, priv->model, &new_iter); + break; + + case GTK_SCROLL_STEP_FORWARD: + case GTK_SCROLL_STEP_DOWN: + case GTK_SCROLL_STEP_RIGHT: + if (active_iter) + { + found = tree_next (combo_box, priv->model, + &iter, &new_iter); + break; + } + G_GNUC_FALLTHROUGH; + + case GTK_SCROLL_PAGE_BACKWARD: + case GTK_SCROLL_PAGE_UP: + case GTK_SCROLL_PAGE_LEFT: + case GTK_SCROLL_START: + found = tree_first (combo_box, priv->model, &new_iter); + break; + + case GTK_SCROLL_NONE: + case GTK_SCROLL_JUMP: + default: + return; + } + + if (found && active_iter) + { + GtkTreePath *old_path; + GtkTreePath *new_path; + + old_path = gtk_tree_model_get_path (priv->model, &iter); + new_path = gtk_tree_model_get_path (priv->model, &new_iter); + + if (gtk_tree_path_compare (old_path, new_path) == 0) + found = FALSE; + + gtk_tree_path_free (old_path); + gtk_tree_path_free (new_path); + } + + if (found) + { + gtk_combo_box_set_active_iter (combo_box, &new_iter); + } + else + { + gtk_widget_error_bell (GTK_WIDGET (combo_box)); + } +} + +static gboolean +gtk_combo_box_mnemonic_activate (GtkWidget *widget, + gboolean group_cycling) +{ + GtkComboBox *combo_box = GTK_COMBO_BOX (widget); + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + + if (priv->has_entry) + { + if (priv->child) + gtk_widget_grab_focus (priv->child); + } + else + gtk_widget_mnemonic_activate (priv->button, group_cycling); + + return TRUE; +} + +static gboolean +gtk_combo_box_grab_focus (GtkWidget *widget) +{ + GtkComboBox *combo_box = GTK_COMBO_BOX (widget); + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + + if (priv->has_entry) + { + if (priv->child) + return gtk_widget_grab_focus (priv->child); + else + return FALSE; + } + else + return gtk_widget_grab_focus (priv->button); +} + +static void +gtk_combo_box_unmap (GtkWidget *widget) +{ + gtk_combo_box_popdown (GTK_COMBO_BOX (widget)); + + GTK_WIDGET_CLASS (gtk_combo_box_parent_class)->unmap (widget); +} + +static void +gtk_combo_box_entry_contents_changed (GtkEntry *entry, + gpointer user_data) +{ + GtkComboBox *combo_box = GTK_COMBO_BOX (user_data); + + /* + * Fixes regression reported in bug #574059. The old functionality relied on + * bug #572478. As a bugfix, we now emit the "changed" signal ourselves + * when the selection was already set to -1. + */ + if (gtk_combo_box_get_active(combo_box) == -1) + g_signal_emit_by_name (combo_box, "changed"); + else + gtk_combo_box_set_active (combo_box, -1); +} + +static void +gtk_combo_box_entry_active_changed (GtkComboBox *combo_box, + gpointer user_data) +{ + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + GtkTreeModel *model; + GtkTreeIter iter; + + if (gtk_combo_box_get_active_iter (combo_box, &iter)) + { + GtkEntry *entry = GTK_ENTRY (priv->child); + + if (entry) + { + GtkTreePath *path; + char *path_str; + char *text = NULL; + + model = gtk_combo_box_get_model (combo_box); + path = gtk_tree_model_get_path (model, &iter); + path_str = gtk_tree_path_to_string (path); + + g_signal_handlers_block_by_func (entry, + gtk_combo_box_entry_contents_changed, + combo_box); + + + g_signal_emit (combo_box, combo_box_signals[FORMAT_ENTRY_TEXT], 0, + path_str, &text); + + gtk_editable_set_text (GTK_EDITABLE (entry), text); + + g_signal_handlers_unblock_by_func (entry, + gtk_combo_box_entry_contents_changed, + combo_box); + + gtk_tree_path_free (path); + g_free (text); + g_free (path_str); + } + } +} + +static char * +gtk_combo_box_format_entry_text (GtkComboBox *combo_box, + const char *path) +{ + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + GtkTreeModel *model; + GtkTreeIter iter; + char *text = NULL; + + if (priv->text_column >= 0) + { + model = gtk_combo_box_get_model (combo_box); + gtk_tree_model_get_iter_from_string (model, &iter, path); + + gtk_tree_model_get (model, &iter, + priv->text_column, &text, + -1); + } + + return text; +} + +static void +gtk_combo_box_constructed (GObject *object) +{ + GtkComboBox *combo_box = GTK_COMBO_BOX (object); + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + + G_OBJECT_CLASS (gtk_combo_box_parent_class)->constructed (object); + + gtk_combo_box_create_child (combo_box); + + if (priv->has_entry) + { + priv->text_renderer = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), + priv->text_renderer, TRUE); + + gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), -1); + } +} + +static void +gtk_combo_box_dispose (GObject* object) +{ + GtkComboBox *combo_box = GTK_COMBO_BOX (object); + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + + if (priv->popup_idle_id > 0) + { + g_source_remove (priv->popup_idle_id); + priv->popup_idle_id = 0; + } + + if (priv->box) + { + /* destroy things (unparent will kill the latest ref from us) + * last unref on button will destroy the arrow + */ + gtk_widget_unparent (priv->box); + priv->box = NULL; + priv->button = NULL; + priv->arrow = NULL; + priv->child = NULL; + priv->cell_view = NULL; + } + + if (priv->row_separator_destroy) + priv->row_separator_destroy (priv->row_separator_data); + + priv->row_separator_func = NULL; + priv->row_separator_data = NULL; + priv->row_separator_destroy = NULL; + + if (priv->popup_widget) + { + /* Stop menu destruction triggering toggle on a now-invalid button */ + g_signal_handlers_disconnect_by_func (priv->popup_widget, + gtk_combo_box_menu_hide, + combo_box); + g_clear_pointer (&priv->popup_widget, gtk_widget_unparent); + } + + gtk_combo_box_unset_model (combo_box); + + G_OBJECT_CLASS (gtk_combo_box_parent_class)->dispose (object); +} + +static gboolean +gtk_cell_editable_key_pressed (GtkEventControllerKey *key, + guint keyval, + guint keycode, + GdkModifierType modifiers, + GtkComboBox *combo_box) +{ + if (keyval == GDK_KEY_Escape) + { + g_object_set (combo_box, + "editing-canceled", TRUE, + NULL); + gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box)); + gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box)); + + return TRUE; + } + else if (keyval == GDK_KEY_Return || + keyval == GDK_KEY_ISO_Enter || + keyval == GDK_KEY_KP_Enter) + { + gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box)); + gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box)); + + return TRUE; + } + + return FALSE; +} + +static void +gtk_combo_box_start_editing (GtkCellEditable *cell_editable, + GdkEvent *event) +{ + GtkComboBox *combo_box = GTK_COMBO_BOX (cell_editable); + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + GtkEventController *controller; + + priv->is_cell_renderer = TRUE; + + controller = gtk_event_controller_key_new (); + g_signal_connect_object (controller, "key-pressed", + G_CALLBACK (gtk_cell_editable_key_pressed), + cell_editable, 0); + + if (priv->cell_view) + { + gtk_widget_add_controller (priv->button, controller); + gtk_widget_grab_focus (priv->button); + } + else + { + gtk_widget_add_controller (priv->child, controller); + + gtk_widget_grab_focus (priv->child); + gtk_widget_set_can_focus (priv->button, FALSE); + } +} + +/** + * gtk_combo_box_set_popup_fixed_width: (attributes org.gtk.Method.set_property=popup-fixed-width) + * @combo_box: a `GtkComboBox` + * @fixed: whether to use a fixed popup width + * + * Specifies whether the popup’s width should be a fixed width. + * + * If @fixed is %TRUE, the popup's width is set to match the + * allocated width of the combo box. + * + * Deprecated: 4.10: Use GtkDropDown + */ +void +gtk_combo_box_set_popup_fixed_width (GtkComboBox *combo_box, + gboolean fixed) +{ + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + + g_return_if_fail (GTK_IS_COMBO_BOX (combo_box)); + + if (priv->popup_fixed_width != fixed) + { + priv->popup_fixed_width = fixed; + + g_object_notify (G_OBJECT (combo_box), "popup-fixed-width"); + } +} + +/** + * gtk_combo_box_get_popup_fixed_width: (attributes org.gtk.Method.get_property=popup-fixed-width) + * @combo_box: a `GtkComboBox` + * + * Gets whether the popup uses a fixed width. + * + * Returns: %TRUE if the popup uses a fixed width + * + * Deprecated: 4.10: Use GtkDropDown + */ +gboolean +gtk_combo_box_get_popup_fixed_width (GtkComboBox *combo_box) +{ + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + + g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE); + + return priv->popup_fixed_width; +} + +/** + * gtk_combo_box_get_row_separator_func: (skip) + * @combo_box: a `GtkComboBox` + * + * Returns the current row separator function. + * + * Returns: (nullable): the current row separator function. + * + * Deprecated: 4.10: Use GtkDropDown + */ +GtkTreeViewRowSeparatorFunc +gtk_combo_box_get_row_separator_func (GtkComboBox *combo_box) +{ + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + + g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL); + + return priv->row_separator_func; +} + +/** + * gtk_combo_box_set_row_separator_func: + * @combo_box: a `GtkComboBox` + * @func: (nullable): a `GtkTreeViewRowSeparatorFunc` + * @data: (nullable): user data to pass to @func + * @destroy: (nullable): destroy notifier for @data + * + * Sets the row separator function, which is used to determine + * whether a row should be drawn as a separator. + * + * If the row separator function is %NULL, no separators are drawn. + * This is the default value. + * + * Deprecated: 4.10: Use GtkDropDown + */ +void +gtk_combo_box_set_row_separator_func (GtkComboBox *combo_box, + GtkTreeViewRowSeparatorFunc func, + gpointer data, + GDestroyNotify destroy) +{ + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + + g_return_if_fail (GTK_IS_COMBO_BOX (combo_box)); + + if (priv->row_separator_destroy) + priv->row_separator_destroy (priv->row_separator_data); + + priv->row_separator_func = func; + priv->row_separator_data = data; + priv->row_separator_destroy = destroy; + + gtk_tree_popover_set_row_separator_func (GTK_TREE_POPOVER (priv->popup_widget), + (GtkTreeViewRowSeparatorFunc)gtk_combo_box_row_separator_func, + combo_box, NULL); + + gtk_widget_queue_draw (GTK_WIDGET (combo_box)); +} + +/** + * gtk_combo_box_set_button_sensitivity: (attributes org.gtk.Method.set_property=button-sensitivity) + * @combo_box: a `GtkComboBox` + * @sensitivity: specify the sensitivity of the dropdown button + * + * Sets whether the dropdown button of the combo box should update + * its sensitivity depending on the model contents. + * + * Deprecated: 4.10: Use GtkDropDown + */ +void +gtk_combo_box_set_button_sensitivity (GtkComboBox *combo_box, + GtkSensitivityType sensitivity) +{ + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + + g_return_if_fail (GTK_IS_COMBO_BOX (combo_box)); + + if (priv->button_sensitivity != sensitivity) + { + priv->button_sensitivity = sensitivity; + gtk_combo_box_update_sensitivity (combo_box); + + g_object_notify (G_OBJECT (combo_box), "button-sensitivity"); + } +} + +/** + * gtk_combo_box_get_button_sensitivity: (attributes org.gtk.Method.get_property=button-sensitivity) + * @combo_box: a `GtkComboBox` + * + * Returns whether the combo box sets the dropdown button + * sensitive or not when there are no items in the model. + * + * Returns: %GTK_SENSITIVITY_ON if the dropdown button + * is sensitive when the model is empty, %GTK_SENSITIVITY_OFF + * if the button is always insensitive or %GTK_SENSITIVITY_AUTO + * if it is only sensitive as long as the model has one item to + * be selected. + * + * Deprecated: 4.10: Use GtkDropDown + */ +GtkSensitivityType +gtk_combo_box_get_button_sensitivity (GtkComboBox *combo_box) +{ + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE); + + return priv->button_sensitivity; +} + + +/** + * gtk_combo_box_get_has_entry: (attributes org.gtk.Method.get_property=has-entry) + * @combo_box: a `GtkComboBox` + * + * Returns whether the combo box has an entry. + * + * Returns: whether there is an entry in @combo_box. + * + * Deprecated: 4.10: Use GtkDropDown + */ +gboolean +gtk_combo_box_get_has_entry (GtkComboBox *combo_box) +{ + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE); + + return priv->has_entry; +} + +/** + * gtk_combo_box_set_entry_text_column: + * @combo_box: A `GtkComboBox` + * @text_column: A column in @model to get the strings from for + * the internal entry + * + * Sets the model column which @combo_box should use to get strings + * from to be @text_column. + * + * For this column no separate + * [class@Gtk.CellRenderer] is needed. + * + * The column @text_column in the model of @combo_box must be of + * type %G_TYPE_STRING. + * + * This is only relevant if @combo_box has been created with + * [property@Gtk.ComboBox:has-entry] as %TRUE. + * + * Deprecated: 4.10: Use GtkDropDown + */ +void +gtk_combo_box_set_entry_text_column (GtkComboBox *combo_box, + int text_column) +{ + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + + g_return_if_fail (GTK_IS_COMBO_BOX (combo_box)); + g_return_if_fail (text_column >= 0); + g_return_if_fail (priv->model == NULL || text_column < gtk_tree_model_get_n_columns (priv->model)); + + if (priv->text_column != text_column) + { + priv->text_column = text_column; + + if (priv->text_renderer != NULL) + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), + priv->text_renderer, + "text", text_column, + NULL); + + g_object_notify (G_OBJECT (combo_box), "entry-text-column"); + } +} + +/** + * gtk_combo_box_get_entry_text_column: + * @combo_box: A `GtkComboBox` + * + * Returns the column which @combo_box is using to get the strings + * from to display in the internal entry. + * + * Returns: A column in the data source model of @combo_box. + * + * Deprecated: 4.10: Use GtkDropDown + */ +int +gtk_combo_box_get_entry_text_column (GtkComboBox *combo_box) +{ + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + + g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), 0); + + return priv->text_column; +} + +static void +gtk_combo_box_buildable_add_child (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const char *type) +{ + if (GTK_IS_CELL_RENDERER (child)) + _gtk_cell_layout_buildable_add_child (buildable, builder, child, type); + else if (GTK_IS_WIDGET (child)) + gtk_combo_box_set_child (GTK_COMBO_BOX (buildable), GTK_WIDGET (child)); + else + parent_buildable_iface->add_child (buildable, builder, child, type); +} + +static gboolean +gtk_combo_box_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const char *tagname, + GtkBuildableParser *parser, + gpointer *data) +{ + if (parent_buildable_iface->custom_tag_start (buildable, builder, child, + tagname, parser, data)) + return TRUE; + + return _gtk_cell_layout_buildable_custom_tag_start (buildable, builder, child, + tagname, parser, data); +} + +static void +gtk_combo_box_buildable_custom_tag_end (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const char *tagname, + gpointer data) +{ + if (!_gtk_cell_layout_buildable_custom_tag_end (buildable, builder, child, tagname, data)) + parent_buildable_iface->custom_tag_end (buildable, builder, child, tagname, data); +} + +static GObject * +gtk_combo_box_buildable_get_internal_child (GtkBuildable *buildable, + GtkBuilder *builder, + const char *childname) +{ + GtkComboBox *combo_box = GTK_COMBO_BOX (buildable); + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + + if (priv->has_entry && strcmp (childname, "entry") == 0) + return G_OBJECT (priv->child); + + return parent_buildable_iface->get_internal_child (buildable, builder, childname); +} + +/** + * gtk_combo_box_set_id_column: (attributes org.gtk.Method.set_property=id-column) + * @combo_box: A `GtkComboBox` + * @id_column: A column in @model to get string IDs for values from + * + * Sets the model column which @combo_box should use to get string IDs + * for values from. + * + * The column @id_column in the model of @combo_box must be of type + * %G_TYPE_STRING. + * + * Deprecated: 4.10: Use GtkDropDown + */ +void +gtk_combo_box_set_id_column (GtkComboBox *combo_box, + int id_column) +{ + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + + g_return_if_fail (GTK_IS_COMBO_BOX (combo_box)); + + if (id_column != priv->id_column) + { + g_return_if_fail (id_column >= 0); + g_return_if_fail (priv->model == NULL || id_column < gtk_tree_model_get_n_columns (priv->model)); + + priv->id_column = id_column; + + g_object_notify (G_OBJECT (combo_box), "id-column"); + g_object_notify (G_OBJECT (combo_box), "active-id"); + } +} + +/** + * gtk_combo_box_get_id_column: (attributes org.gtk.Method.get_property=id-column) + * @combo_box: A `GtkComboBox` + * + * Returns the column which @combo_box is using to get string IDs + * for values from. + * + * Returns: A column in the data source model of @combo_box. + * + * Deprecated: 4.10: Use GtkDropDown + */ +int +gtk_combo_box_get_id_column (GtkComboBox *combo_box) +{ + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + + g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), 0); + + return priv->id_column; +} + +/** + * gtk_combo_box_get_active_id: (attributes org.gtk.Method.get_property=active-id) + * @combo_box: a `GtkComboBox` + * + * Returns the ID of the active row of @combo_box. + * + * This value is taken from the active row and the column specified + * by the [property@Gtk.ComboBox:id-column] property of @combo_box + * (see [method@Gtk.ComboBox.set_id_column]). + * + * The returned value is an interned string which means that you can + * compare the pointer by value to other interned strings and that you + * must not free it. + * + * If the [property@Gtk.ComboBox:id-column] property of @combo_box is + * not set, or if no row is active, or if the active row has a %NULL + * ID value, then %NULL is returned. + * + * Returns: (nullable): the ID of the active row + * + * Deprecated: 4.10: Use GtkDropDown + */ +const char * +gtk_combo_box_get_active_id (GtkComboBox *combo_box) +{ + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + GtkTreeModel *model; + GtkTreeIter iter; + int column; + + g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL); + + column = priv->id_column; + + if (column < 0) + return NULL; + + model = gtk_combo_box_get_model (combo_box); + g_return_val_if_fail (gtk_tree_model_get_column_type (model, column) == + G_TYPE_STRING, NULL); + + if (gtk_combo_box_get_active_iter (combo_box, &iter)) + { + const char *interned; + char *id; + + gtk_tree_model_get (model, &iter, column, &id, -1); + interned = g_intern_string (id); + g_free (id); + + return interned; + } + + return NULL; +} + +/** + * gtk_combo_box_set_active_id: (attributes org.gtk.Method.set_property=active-id) + * @combo_box: a `GtkComboBox` + * @active_id: (nullable): the ID of the row to select + * + * Changes the active row of @combo_box to the one that has an ID equal to + * @active_id. + * + * If @active_id is %NULL, the active row is unset. Rows having + * a %NULL ID string cannot be made active by this function. + * + * If the [property@Gtk.ComboBox:id-column] property of @combo_box is + * unset or if no row has the given ID then the function does nothing + * and returns %FALSE. + * + * Returns: %TRUE if a row with a matching ID was found. If a %NULL + * @active_id was given to unset the active row, the function + * always returns %TRUE. + * + * Deprecated: 4.10: Use GtkDropDown + */ +gboolean +gtk_combo_box_set_active_id (GtkComboBox *combo_box, + const char *active_id) +{ + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + GtkTreeModel *model; + GtkTreeIter iter; + gboolean match = FALSE; + int column; + + g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE); + + if (active_id == NULL) + { + gtk_combo_box_set_active (combo_box, -1); + return TRUE; /* active row was successfully unset */ + } + + column = priv->id_column; + + if (column < 0) + return FALSE; + + model = gtk_combo_box_get_model (combo_box); + g_return_val_if_fail (gtk_tree_model_get_column_type (model, column) == + G_TYPE_STRING, FALSE); + + if (gtk_tree_model_get_iter_first (model, &iter)) + do { + char *id; + + gtk_tree_model_get (model, &iter, column, &id, -1); + if (id != NULL) + match = strcmp (id, active_id) == 0; + g_free (id); + + if (match) + { + gtk_combo_box_set_active_iter (combo_box, &iter); + break; + } + } while (gtk_tree_model_iter_next (model, &iter)); + + g_object_notify (G_OBJECT (combo_box), "active-id"); + + return match; +} + +GtkWidget * +gtk_combo_box_get_popup (GtkComboBox *combo_box) +{ + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + + return priv->popup_widget; +} + +/** + * gtk_combo_box_set_child: (attributes org.gtk.Method.set_property=child) + * @combo_box: a `GtkComboBox` + * @child: (nullable): the child widget + * + * Sets the child widget of @combo_box. + * + * Deprecated: 4.10: Use GtkDropDown + */ +void +gtk_combo_box_set_child (GtkComboBox *combo_box, + GtkWidget *child) +{ + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + + g_return_if_fail (GTK_IS_COMBO_BOX (combo_box)); + g_return_if_fail (child == NULL || GTK_IS_WIDGET (child)); + + if (priv->child) + gtk_combo_box_remove (combo_box, priv->child); + + if (child) + gtk_combo_box_add (combo_box, child); + + g_object_notify (G_OBJECT (combo_box), "child"); +} + +/** + * gtk_combo_box_get_child: (attributes org.gtk.Method.get_property=child) + * @combo_box: a `GtkComboBox` + * + * Gets the child widget of @combo_box. + * + * Returns: (nullable) (transfer none): the child widget of @combo_box + * + * Deprecated: 4.10: Use GtkDropDown + */ +GtkWidget * +gtk_combo_box_get_child (GtkComboBox *combo_box) +{ + GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); + + g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL); + + return priv->child; +} + diff --git a/gtk/deprecated/gtkcombobox.h b/gtk/deprecated/gtkcombobox.h new file mode 100644 index 0000000000..0f2b5c31cb --- /dev/null +++ b/gtk/deprecated/gtkcombobox.h @@ -0,0 +1,161 @@ +/* gtkcombobox.h + * Copyright (C) 2002, 2003 Kristian Rietveld + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#ifndef __GTK_COMBO_BOX_H__ +#define __GTK_COMBO_BOX_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_COMBO_BOX (gtk_combo_box_get_type ()) +#define GTK_COMBO_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_COMBO_BOX, GtkComboBox)) +#define GTK_COMBO_BOX_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST ((vtable), GTK_TYPE_COMBO_BOX, GtkComboBoxClass)) +#define GTK_IS_COMBO_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_COMBO_BOX)) +#define GTK_IS_COMBO_BOX_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), GTK_TYPE_COMBO_BOX)) +#define GTK_COMBO_BOX_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), GTK_TYPE_COMBO_BOX, GtkComboBoxClass)) + +typedef struct _GtkComboBox GtkComboBox; +typedef struct _GtkComboBoxClass GtkComboBoxClass; + +struct _GtkComboBox +{ + GtkWidget parent_instance; +}; + +/** + * GtkComboBoxClass: + * @parent_class: The parent class. + * @changed: Signal is emitted when the active item is changed. + * @format_entry_text: Signal which allows you to change how the text + * displayed in a combo box’s entry is displayed. + */ +struct _GtkComboBoxClass +{ + GtkWidgetClass parent_class; + + /*< public >*/ + + /* signals */ + void (* changed) (GtkComboBox *combo_box); + char *(* format_entry_text) (GtkComboBox *combo_box, + const char *path); + void (* activate) (GtkComboBox *combo_box); + + /*< private >*/ + + gpointer padding[7]; +}; + + +/* construction */ +GDK_AVAILABLE_IN_ALL +GType gtk_combo_box_get_type (void) G_GNUC_CONST; +GDK_DEPRECATED_IN_4_10 +GtkWidget *gtk_combo_box_new (void); +GDK_DEPRECATED_IN_4_10 +GtkWidget *gtk_combo_box_new_with_entry (void); +GDK_DEPRECATED_IN_4_10 +GtkWidget *gtk_combo_box_new_with_model (GtkTreeModel *model); +GDK_DEPRECATED_IN_4_10 +GtkWidget *gtk_combo_box_new_with_model_and_entry (GtkTreeModel *model); + +/* get/set active item */ +GDK_DEPRECATED_IN_4_10 +int gtk_combo_box_get_active (GtkComboBox *combo_box); +GDK_DEPRECATED_IN_4_10 +void gtk_combo_box_set_active (GtkComboBox *combo_box, + int index_); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_combo_box_get_active_iter (GtkComboBox *combo_box, + GtkTreeIter *iter); +GDK_DEPRECATED_IN_4_10 +void gtk_combo_box_set_active_iter (GtkComboBox *combo_box, + GtkTreeIter *iter); + +/* getters and setters */ +GDK_DEPRECATED_IN_4_10 +void gtk_combo_box_set_model (GtkComboBox *combo_box, + GtkTreeModel *model); +GDK_DEPRECATED_IN_4_10 +GtkTreeModel *gtk_combo_box_get_model (GtkComboBox *combo_box); + +GDK_DEPRECATED_IN_4_10 +GtkTreeViewRowSeparatorFunc gtk_combo_box_get_row_separator_func (GtkComboBox *combo_box); +GDK_DEPRECATED_IN_4_10 +void gtk_combo_box_set_row_separator_func (GtkComboBox *combo_box, + GtkTreeViewRowSeparatorFunc func, + gpointer data, + GDestroyNotify destroy); + +GDK_DEPRECATED_IN_4_10 +void gtk_combo_box_set_button_sensitivity (GtkComboBox *combo_box, + GtkSensitivityType sensitivity); +GDK_DEPRECATED_IN_4_10 +GtkSensitivityType gtk_combo_box_get_button_sensitivity (GtkComboBox *combo_box); + +GDK_DEPRECATED_IN_4_10 +gboolean gtk_combo_box_get_has_entry (GtkComboBox *combo_box); +GDK_DEPRECATED_IN_4_10 +void gtk_combo_box_set_entry_text_column (GtkComboBox *combo_box, + int text_column); +GDK_DEPRECATED_IN_4_10 +int gtk_combo_box_get_entry_text_column (GtkComboBox *combo_box); + +GDK_DEPRECATED_IN_4_10 +void gtk_combo_box_set_popup_fixed_width (GtkComboBox *combo_box, + gboolean fixed); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_combo_box_get_popup_fixed_width (GtkComboBox *combo_box); + +/* programmatic control */ +GDK_DEPRECATED_IN_4_10 +void gtk_combo_box_popup (GtkComboBox *combo_box); +GDK_DEPRECATED_IN_4_10 +void gtk_combo_box_popup_for_device (GtkComboBox *combo_box, + GdkDevice *device); +GDK_DEPRECATED_IN_4_10 +void gtk_combo_box_popdown (GtkComboBox *combo_box); + +GDK_DEPRECATED_IN_4_10 +int gtk_combo_box_get_id_column (GtkComboBox *combo_box); +GDK_DEPRECATED_IN_4_10 +void gtk_combo_box_set_id_column (GtkComboBox *combo_box, + int id_column); +GDK_DEPRECATED_IN_4_10 +const char * gtk_combo_box_get_active_id (GtkComboBox *combo_box); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_combo_box_set_active_id (GtkComboBox *combo_box, + const char *active_id); + +GDK_DEPRECATED_IN_4_10 +void gtk_combo_box_set_child (GtkComboBox *combo_box, + GtkWidget *child); +GDK_DEPRECATED_IN_4_10 +GtkWidget * gtk_combo_box_get_child (GtkComboBox *combo_box); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkComboBox, g_object_unref) + +G_END_DECLS + +#endif /* __GTK_COMBO_BOX_H__ */ diff --git a/gtk/deprecated/gtkcomboboxprivate.h b/gtk/deprecated/gtkcomboboxprivate.h new file mode 100644 index 0000000000..6af67e06c6 --- /dev/null +++ b/gtk/deprecated/gtkcomboboxprivate.h @@ -0,0 +1,29 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 2014 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#ifndef __GTK_COMBO_BOX_PRIVATE_H__ +#define __GTK_COMBO_BOX_PRIVATE_H__ + +#include "gtkcombobox.h" + +G_BEGIN_DECLS + +GtkWidget *gtk_combo_box_get_popup (GtkComboBox *combo_box); + +G_END_DECLS + +#endif /* __GTK_COMBO_BOX_PRIVATE_H__ */ diff --git a/gtk/deprecated/gtkcomboboxtext.c b/gtk/deprecated/gtkcomboboxtext.c new file mode 100644 index 0000000000..9d48f5aca4 --- /dev/null +++ b/gtk/deprecated/gtkcomboboxtext.c @@ -0,0 +1,643 @@ +/* GTK - The GIMP Toolkit + * + * Copyright (C) 2010 Christian Dywan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#include "config.h" + +#include "gtkcomboboxtext.h" +#include "gtkcombobox.h" +#include "gtkcellrenderertext.h" +#include "gtkcelllayout.h" +#include "gtkbuildable.h" +#include "gtkbuilderprivate.h" +#include "gtkliststore.h" + +#include + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + +/** + * GtkComboBoxText: + * + * A `GtkComboBoxText` is a simple variant of `GtkComboBox` for text-only + * use cases. + * + * ![An example GtkComboBoxText](combo-box-text.png) + * + * `GtkComboBoxText` hides the model-view complexity of `GtkComboBox`. + * + * To create a `GtkComboBoxText`, use [ctor@Gtk.ComboBoxText.new] or + * [ctor@Gtk.ComboBoxText.new_with_entry]. + * + * You can add items to a `GtkComboBoxText` with + * [method@Gtk.ComboBoxText.append_text], + * [method@Gtk.ComboBoxText.insert_text] or + * [method@Gtk.ComboBoxText.prepend_text] and remove options with + * [method@Gtk.ComboBoxText.remove]. + * + * If the `GtkComboBoxText` contains an entry (via the + * [property@Gtk.ComboBox:has-entry] property), its contents can be retrieved + * using [method@Gtk.ComboBoxText.get_active_text]. + * + * You should not call [method@Gtk.ComboBox.set_model] or attempt to pack more + * cells into this combo box via its [iface@Gtk.CellLayout] interface. + * + * # GtkComboBoxText as GtkBuildable + * + * The `GtkComboBoxText` implementation of the `GtkBuildable` interface supports + * adding items directly using the element and specifying + * elements for each item. Each element can specify the “id” + * corresponding to the appended text and also supports the regular + * translation attributes “translatable”, “context” and “comments”. + * + * Here is a UI definition fragment specifying `GtkComboBoxText` items: + * ```xml + * + * + * Factory + * Home + * Subway + * + * + * ``` + * + * # CSS nodes + * + * ``` + * combobox + * ╰── box.linked + * ├── entry.combo + * ├── button.combo + * ╰── window.popup + * ``` + * + * `GtkComboBoxText` has a single CSS node with name combobox. It adds + * the style class .combo to the main CSS nodes of its entry and button + * children, and the .linked class to the node of its internal box. + */ + +typedef struct _GtkComboBoxTextClass GtkComboBoxTextClass; + +struct _GtkComboBoxText +{ + GtkComboBox parent_instance; +}; + +struct _GtkComboBoxTextClass +{ + GtkComboBoxClass parent_class; +}; + + +static void gtk_combo_box_text_buildable_interface_init (GtkBuildableIface *iface); +static gboolean gtk_combo_box_text_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const char *tagname, + GtkBuildableParser *parser, + gpointer *data); + +static void gtk_combo_box_text_buildable_custom_finished (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const char *tagname, + gpointer user_data); + + +static GtkBuildableIface *buildable_parent_iface = NULL; + +G_DEFINE_TYPE_WITH_CODE (GtkComboBoxText, gtk_combo_box_text, GTK_TYPE_COMBO_BOX, + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, + gtk_combo_box_text_buildable_interface_init)); + +static void +gtk_combo_box_text_constructed (GObject *object) +{ + const int text_column = 0; + + G_OBJECT_CLASS (gtk_combo_box_text_parent_class)->constructed (object); + + gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX (object), text_column); + gtk_combo_box_set_id_column (GTK_COMBO_BOX (object), 1); + + if (!gtk_combo_box_get_has_entry (GTK_COMBO_BOX (object))) + { + GtkCellRenderer *cell; + + cell = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (object), cell, TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (object), cell, + "text", text_column, + NULL); + } +} + +static void +gtk_combo_box_text_init (GtkComboBoxText *combo_box) +{ + GtkListStore *store; + + store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING); + gtk_combo_box_set_model (GTK_COMBO_BOX (combo_box), GTK_TREE_MODEL (store)); + g_object_unref (store); +} + +static void +gtk_combo_box_text_class_init (GtkComboBoxTextClass *klass) +{ + GObjectClass *object_class; + + object_class = (GObjectClass *)klass; + object_class->constructed = gtk_combo_box_text_constructed; +} + +static void +gtk_combo_box_text_buildable_interface_init (GtkBuildableIface *iface) +{ + buildable_parent_iface = g_type_interface_peek_parent (iface); + + iface->custom_tag_start = gtk_combo_box_text_buildable_custom_tag_start; + iface->custom_finished = gtk_combo_box_text_buildable_custom_finished; +} + +typedef struct { + GtkBuilder *builder; + GObject *object; + const char *domain; + char *id; + + GString *string; + + char *context; + guint translatable : 1; + + guint is_text : 1; +} ItemParserData; + +static void +item_start_element (GtkBuildableParseContext *context, + const char *element_name, + const char **names, + const char **values, + gpointer user_data, + GError **error) +{ + ItemParserData *data = (ItemParserData*)user_data; + + if (strcmp (element_name, "items") == 0) + { + if (!_gtk_builder_check_parent (data->builder, context, "object", error)) + return; + + if (!g_markup_collect_attributes (element_name, names, values, error, + G_MARKUP_COLLECT_INVALID, NULL, NULL, + G_MARKUP_COLLECT_INVALID)) + _gtk_builder_prefix_error (data->builder, context, error); + } + else if (strcmp (element_name, "item") == 0) + { + const char *id = NULL; + gboolean translatable = FALSE; + const char *msg_context = NULL; + + if (!_gtk_builder_check_parent (data->builder, context, "items", error)) + return; + + if (!g_markup_collect_attributes (element_name, names, values, error, + G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "id", &id, + G_MARKUP_COLLECT_BOOLEAN|G_MARKUP_COLLECT_OPTIONAL, "translatable", &translatable, + G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "comments", NULL, + G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "context", &msg_context, + G_MARKUP_COLLECT_INVALID)) + { + _gtk_builder_prefix_error (data->builder, context, error); + return; + } + + data->is_text = TRUE; + data->translatable = translatable; + data->context = g_strdup (msg_context); + data->id = g_strdup (id); + } + else + { + _gtk_builder_error_unhandled_tag (data->builder, context, + "GtkComboBoxText", element_name, + error); + } +} + +static void +item_text (GtkBuildableParseContext *context, + const char *text, + gsize text_len, + gpointer user_data, + GError **error) +{ + ItemParserData *data = (ItemParserData*)user_data; + + if (data->is_text) + g_string_append_len (data->string, text, text_len); +} + +static void +item_end_element (GtkBuildableParseContext *context, + const char *element_name, + gpointer user_data, + GError **error) +{ + ItemParserData *data = (ItemParserData*)user_data; + + /* Append the translated strings */ + if (data->string->len) + { + if (data->translatable) + { + const char *translated; + + translated = _gtk_builder_parser_translate (data->domain, + data->context, + data->string->str); + g_string_assign (data->string, translated); + } + + gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (data->object), data->id, data->string->str); + } + + data->translatable = FALSE; + g_string_set_size (data->string, 0); + g_clear_pointer (&data->context, g_free); + g_clear_pointer (&data->id, g_free); + data->is_text = FALSE; +} + +static const GtkBuildableParser item_parser = + { + item_start_element, + item_end_element, + item_text + }; + +static gboolean +gtk_combo_box_text_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const char *tagname, + GtkBuildableParser *parser, + gpointer *parser_data) +{ + if (buildable_parent_iface->custom_tag_start (buildable, builder, child, + tagname, parser, parser_data)) + return TRUE; + + if (strcmp (tagname, "items") == 0) + { + ItemParserData *data; + + data = g_slice_new0 (ItemParserData); + data->builder = g_object_ref (builder); + data->object = (GObject *) g_object_ref (buildable); + data->domain = gtk_builder_get_translation_domain (builder); + data->string = g_string_new (""); + + *parser = item_parser; + *parser_data = data; + + return TRUE; + } + + return FALSE; +} + +static void +gtk_combo_box_text_buildable_custom_finished (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const char *tagname, + gpointer user_data) +{ + ItemParserData *data; + + buildable_parent_iface->custom_finished (buildable, builder, child, + tagname, user_data); + + if (strcmp (tagname, "items") == 0) + { + data = (ItemParserData*)user_data; + + g_object_unref (data->object); + g_object_unref (data->builder); + g_string_free (data->string, TRUE); + g_slice_free (ItemParserData, data); + } +} + +/** + * gtk_combo_box_text_new: + * + * Creates a new `GtkComboBoxText`. + * + * Returns: A new `GtkComboBoxText` + * + * Deprecated: 4.10: Use GtkDropDown + */ +GtkWidget * +gtk_combo_box_text_new (void) +{ + return g_object_new (GTK_TYPE_COMBO_BOX_TEXT, + NULL); +} + +/** + * gtk_combo_box_text_new_with_entry: + * + * Creates a new `GtkComboBoxText` with an entry. + * + * Returns: a new `GtkComboBoxText` + * + * Deprecated: 4.10: Use GtkDropDown + */ +GtkWidget * +gtk_combo_box_text_new_with_entry (void) +{ + return g_object_new (GTK_TYPE_COMBO_BOX_TEXT, + "has-entry", TRUE, + NULL); +} + +/** + * gtk_combo_box_text_append_text: + * @combo_box: A `GtkComboBoxText` + * @text: A string + * + * Appends @text to the list of strings stored in @combo_box. + * + * This is the same as calling [method@Gtk.ComboBoxText.insert_text] + * with a position of -1. + * + * Deprecated: 4.10: Use GtkDropDown + */ +void +gtk_combo_box_text_append_text (GtkComboBoxText *combo_box, + const char *text) +{ + gtk_combo_box_text_insert (combo_box, -1, NULL, text); +} + +/** + * gtk_combo_box_text_prepend_text: + * @combo_box: A `GtkComboBox` + * @text: A string + * + * Prepends @text to the list of strings stored in @combo_box. + * + * This is the same as calling [method@Gtk.ComboBoxText.insert_text] + * with a position of 0. + * + * Deprecated: 4.10: Use GtkDropDown + */ +void +gtk_combo_box_text_prepend_text (GtkComboBoxText *combo_box, + const char *text) +{ + gtk_combo_box_text_insert (combo_box, 0, NULL, text); +} + +/** + * gtk_combo_box_text_insert_text: + * @combo_box: A `GtkComboBoxText` + * @position: An index to insert @text + * @text: A string + * + * Inserts @text at @position in the list of strings stored in @combo_box. + * + * If @position is negative then @text is appended. + * + * This is the same as calling [method@Gtk.ComboBoxText.insert] + * with a %NULL ID string. + * + * Deprecated: 4.10: Use GtkDropDown + */ +void +gtk_combo_box_text_insert_text (GtkComboBoxText *combo_box, + int position, + const char *text) +{ + gtk_combo_box_text_insert (combo_box, position, NULL, text); +} + +/** + * gtk_combo_box_text_append: + * @combo_box: A `GtkComboBoxText` + * @id: (nullable): a string ID for this value + * @text: A string + * + * Appends @text to the list of strings stored in @combo_box. + * + * If @id is non-%NULL then it is used as the ID of the row. + * + * This is the same as calling [method@Gtk.ComboBoxText.insert] + * with a position of -1. + * + * Deprecated: 4.10: Use GtkDropDown + */ +void +gtk_combo_box_text_append (GtkComboBoxText *combo_box, + const char *id, + const char *text) +{ + gtk_combo_box_text_insert (combo_box, -1, id, text); +} + +/** + * gtk_combo_box_text_prepend: + * @combo_box: A `GtkComboBox` + * @id: (nullable): a string ID for this value + * @text: a string + * + * Prepends @text to the list of strings stored in @combo_box. + * + * If @id is non-%NULL then it is used as the ID of the row. + * + * This is the same as calling [method@Gtk.ComboBoxText.insert] + * with a position of 0. + * + * Deprecated: 4.10: Use GtkDropDown + */ +void +gtk_combo_box_text_prepend (GtkComboBoxText *combo_box, + const char *id, + const char *text) +{ + gtk_combo_box_text_insert (combo_box, 0, id, text); +} + + +/** + * gtk_combo_box_text_insert: + * @combo_box: A `GtkComboBoxText` + * @position: An index to insert @text + * @id: (nullable): a string ID for this value + * @text: A string to display + * + * Inserts @text at @position in the list of strings stored in @combo_box. + * + * If @id is non-%NULL then it is used as the ID of the row. + * See [property@Gtk.ComboBox:id-column]. + * + * If @position is negative then @text is appended. + * + * Deprecated: 4.10: Use GtkDropDown + */ +void +gtk_combo_box_text_insert (GtkComboBoxText *combo_box, + int position, + const char *id, + const char *text) +{ + GtkListStore *store; + GtkTreeIter iter; + int text_column; + + g_return_if_fail (GTK_IS_COMBO_BOX_TEXT (combo_box)); + g_return_if_fail (text != NULL); + + store = GTK_LIST_STORE (gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box))); + g_return_if_fail (GTK_IS_LIST_STORE (store)); + + text_column = gtk_combo_box_get_entry_text_column (GTK_COMBO_BOX (combo_box)); + + if (gtk_combo_box_get_has_entry (GTK_COMBO_BOX (combo_box))) + g_return_if_fail (text_column >= 0); + else if (text_column < 0) + text_column = 0; + + g_return_if_fail (gtk_tree_model_get_column_type (GTK_TREE_MODEL (store), text_column) == G_TYPE_STRING); + + if (position < 0) + gtk_list_store_append (store, &iter); + else + gtk_list_store_insert (store, &iter, position); + + gtk_list_store_set (store, &iter, text_column, text, -1); + + if (id != NULL) + { + int id_column; + + id_column = gtk_combo_box_get_id_column (GTK_COMBO_BOX (combo_box)); + g_return_if_fail (id_column >= 0); + g_return_if_fail (gtk_tree_model_get_column_type (GTK_TREE_MODEL (store), id_column) == G_TYPE_STRING); + + gtk_list_store_set (store, &iter, id_column, id, -1); + } +} + +/** + * gtk_combo_box_text_remove: + * @combo_box: A `GtkComboBox` + * @position: Index of the item to remove + * + * Removes the string at @position from @combo_box. + * + * Deprecated: 4.10: Use GtkDropDown + */ +void +gtk_combo_box_text_remove (GtkComboBoxText *combo_box, + int position) +{ + GtkTreeModel *model; + GtkListStore *store; + GtkTreeIter iter; + + g_return_if_fail (GTK_IS_COMBO_BOX_TEXT (combo_box)); + g_return_if_fail (position >= 0); + + model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box)); + store = GTK_LIST_STORE (model); + g_return_if_fail (GTK_IS_LIST_STORE (store)); + + if (gtk_tree_model_iter_nth_child (model, &iter, NULL, position)) + gtk_list_store_remove (store, &iter); +} + +/** + * gtk_combo_box_text_remove_all: + * @combo_box: A `GtkComboBoxText` + * + * Removes all the text entries from the combo box. + * + * Deprecated: 4.10: Use GtkDropDown + */ +void +gtk_combo_box_text_remove_all (GtkComboBoxText *combo_box) +{ + GtkListStore *store; + + g_return_if_fail (GTK_IS_COMBO_BOX_TEXT (combo_box)); + + store = GTK_LIST_STORE (gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box))); + gtk_list_store_clear (store); +} + +/** + * gtk_combo_box_text_get_active_text: + * @combo_box: A `GtkComboBoxText` + * + * Returns the currently active string in @combo_box. + * + * If no row is currently selected, %NULL is returned. + * If @combo_box contains an entry, this function will + * return its contents (which will not necessarily + * be an item from the list). + * + * Returns: (nullable) (transfer full): a newly allocated + * string containing the currently active text. + * Must be freed with g_free(). + * + * Deprecated: 4.10: Use GtkDropDown + */ +char * +gtk_combo_box_text_get_active_text (GtkComboBoxText *combo_box) +{ + GtkTreeIter iter; + char *text = NULL; + + g_return_val_if_fail (GTK_IS_COMBO_BOX_TEXT (combo_box), NULL); + + if (gtk_combo_box_get_has_entry (GTK_COMBO_BOX (combo_box))) + { + GtkWidget *entry; + + entry = gtk_combo_box_get_child (GTK_COMBO_BOX (combo_box)); + text = g_strdup (gtk_editable_get_text (GTK_EDITABLE (entry))); + } + else if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo_box), &iter)) + { + GtkTreeModel *model; + int text_column; + + model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box)); + g_return_val_if_fail (GTK_IS_LIST_STORE (model), NULL); + text_column = gtk_combo_box_get_entry_text_column (GTK_COMBO_BOX (combo_box)); + g_return_val_if_fail (text_column >= 0, NULL); + g_return_val_if_fail (gtk_tree_model_get_column_type (model, text_column) == G_TYPE_STRING, NULL); + gtk_tree_model_get (model, &iter, text_column, &text, -1); + } + + return text; +} diff --git a/gtk/deprecated/gtkcomboboxtext.h b/gtk/deprecated/gtkcomboboxtext.h new file mode 100644 index 0000000000..c879af4ceb --- /dev/null +++ b/gtk/deprecated/gtkcomboboxtext.h @@ -0,0 +1,78 @@ +/* GTK - The GIMP Toolkit + * + * Copyright (C) 2010 Christian Dywan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#ifndef __GTK_COMBO_BOX_TEXT_H__ +#define __GTK_COMBO_BOX_TEXT_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_COMBO_BOX_TEXT (gtk_combo_box_text_get_type ()) +#define GTK_COMBO_BOX_TEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_COMBO_BOX_TEXT, GtkComboBoxText)) +#define GTK_IS_COMBO_BOX_TEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_COMBO_BOX_TEXT)) + +typedef struct _GtkComboBoxText GtkComboBoxText; + +GDK_AVAILABLE_IN_ALL +GType gtk_combo_box_text_get_type (void) G_GNUC_CONST; +GDK_DEPRECATED_IN_4_10 +GtkWidget* gtk_combo_box_text_new (void); +GDK_DEPRECATED_IN_4_10 +GtkWidget* gtk_combo_box_text_new_with_entry (void); +GDK_DEPRECATED_IN_4_10 +void gtk_combo_box_text_append_text (GtkComboBoxText *combo_box, + const char *text); +GDK_DEPRECATED_IN_4_10 +void gtk_combo_box_text_insert_text (GtkComboBoxText *combo_box, + int position, + const char *text); +GDK_DEPRECATED_IN_4_10 +void gtk_combo_box_text_prepend_text (GtkComboBoxText *combo_box, + const char *text); +GDK_DEPRECATED_IN_4_10 +void gtk_combo_box_text_remove (GtkComboBoxText *combo_box, + int position); +GDK_DEPRECATED_IN_4_10 +void gtk_combo_box_text_remove_all (GtkComboBoxText *combo_box); +GDK_DEPRECATED_IN_4_10 +char *gtk_combo_box_text_get_active_text (GtkComboBoxText *combo_box); + +GDK_DEPRECATED_IN_4_10 +void gtk_combo_box_text_insert (GtkComboBoxText *combo_box, + int position, + const char *id, + const char *text); +GDK_DEPRECATED_IN_4_10 +void gtk_combo_box_text_append (GtkComboBoxText *combo_box, + const char *id, + const char *text); +GDK_DEPRECATED_IN_4_10 +void gtk_combo_box_text_prepend (GtkComboBoxText *combo_box, + const char *id, + const char *text); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkComboBoxText, g_object_unref) + +G_END_DECLS + +#endif /* __GTK_COMBO_BOX_TEXT_H__ */ diff --git a/gtk/deprecated/gtkentrycompletion.h b/gtk/deprecated/gtkentrycompletion.h index 042b302ed7..f4cc55508c 100644 --- a/gtk/deprecated/gtkentrycompletion.h +++ b/gtk/deprecated/gtkentrycompletion.h @@ -23,11 +23,11 @@ #endif #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include G_BEGIN_DECLS diff --git a/gtk/deprecated/gtkiconview.c b/gtk/deprecated/gtkiconview.c new file mode 100644 index 0000000000..0e4e31933c --- /dev/null +++ b/gtk/deprecated/gtkiconview.c @@ -0,0 +1,6800 @@ +/* gtkiconview.c + * Copyright (C) 2002, 2004 Anders Carlsson + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#include "config.h" + +#include "gtkiconviewprivate.h" + +#include "gtkadjustmentprivate.h" +#include "gtkcellareabox.h" +#include "gtkcellareacontext.h" +#include "gtkcelllayout.h" +#include "gtkcellrenderer.h" +#include "gtkcellrendererpixbuf.h" +#include "gtkcellrenderertext.h" +#include "gtkdragsourceprivate.h" +#include "gtkentry.h" +#include "gtkmain.h" +#include "gtkmarshalers.h" +#include "gtkorientable.h" +#include "gtkprivate.h" +#include "gtkscrollable.h" +#include "gtksizerequest.h" +#include "gtksnapshot.h" +#include "gtkstylecontextprivate.h" +#include "gtktreednd.h" +#include "gtktypebuiltins.h" +#include "gtkwidgetprivate.h" +#include "gtkwindow.h" +#include "gtkeventcontrollerkey.h" +#include "gtkdragsource.h" +#include "gtkdragicon.h" +#include "gtknative.h" + +#include + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + +/** + * GtkIconView: + * + * `GtkIconView` is a widget which displays data in a grid of icons. + * + * `GtkIconView` provides an alternative view on a `GtkTreeModel`. + * It displays the model as a grid of icons with labels. Like + * [class@Gtk.TreeView], it allows to select one or multiple items + * (depending on the selection mode, see [method@Gtk.IconView.set_selection_mode]). + * In addition to selection with the arrow keys, `GtkIconView` supports + * rubberband selection, which is controlled by dragging the pointer. + * + * Note that if the tree model is backed by an actual tree store (as + * opposed to a flat list where the mapping to icons is obvious), + * `GtkIconView` will only display the first level of the tree and + * ignore the tree’s branches. + * + * # CSS nodes + * + * ``` + * iconview.view + * ╰── [rubberband] + * ``` + * + * `GtkIconView` has a single CSS node with name iconview and style class .view. + * For rubberband selection, a subnode with name rubberband is used. + */ + +#define SCROLL_EDGE_SIZE 15 + +typedef struct _GtkIconViewChild GtkIconViewChild; +struct _GtkIconViewChild +{ + GtkWidget *widget; + GdkRectangle area; +}; + +/* Signals */ +enum +{ + ITEM_ACTIVATED, + SELECTION_CHANGED, + SELECT_ALL, + UNSELECT_ALL, + SELECT_CURSOR_ITEM, + TOGGLE_CURSOR_ITEM, + MOVE_CURSOR, + ACTIVATE_CURSOR_ITEM, + LAST_SIGNAL +}; + +/* Properties */ +enum +{ + PROP_0, + PROP_PIXBUF_COLUMN, + PROP_TEXT_COLUMN, + PROP_MARKUP_COLUMN, + PROP_SELECTION_MODE, + PROP_ITEM_ORIENTATION, + PROP_MODEL, + PROP_COLUMNS, + PROP_ITEM_WIDTH, + PROP_SPACING, + PROP_ROW_SPACING, + PROP_COLUMN_SPACING, + PROP_MARGIN, + PROP_REORDERABLE, + PROP_TOOLTIP_COLUMN, + PROP_ITEM_PADDING, + PROP_CELL_AREA, + PROP_HADJUSTMENT, + PROP_VADJUSTMENT, + PROP_HSCROLL_POLICY, + PROP_VSCROLL_POLICY, + PROP_ACTIVATE_ON_SINGLE_CLICK +}; + +/* GObject vfuncs */ +static void gtk_icon_view_cell_layout_init (GtkCellLayoutIface *iface); +static void gtk_icon_view_dispose (GObject *object); +static void gtk_icon_view_constructed (GObject *object); +static void gtk_icon_view_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gtk_icon_view_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +/* GtkWidget vfuncs */ +static GtkSizeRequestMode gtk_icon_view_get_request_mode (GtkWidget *widget); +static void gtk_icon_view_measure (GtkWidget *widget, + GtkOrientation orientation, + int for_size, + int *minimum, + int *natural, + int *minimum_baseline, + int *natural_baseline); +static void gtk_icon_view_size_allocate (GtkWidget *widget, + int width, + int height, + int baseline); +static void gtk_icon_view_snapshot (GtkWidget *widget, + GtkSnapshot *snapshot); +static void gtk_icon_view_motion (GtkEventController *controller, + double x, + double y, + gpointer user_data); +static void gtk_icon_view_leave (GtkEventController *controller, + gpointer user_data); +static void gtk_icon_view_button_press (GtkGestureClick *gesture, + int n_press, + double x, + double y, + gpointer user_data); +static void gtk_icon_view_button_release (GtkGestureClick *gesture, + int n_press, + double x, + double y, + gpointer user_data); +static gboolean gtk_icon_view_key_pressed (GtkEventControllerKey *controller, + guint keyval, + guint keycode, + GdkModifierType state, + GtkWidget *widget); + +static void gtk_icon_view_remove (GtkIconView *icon_view, + GtkWidget *widget); + +/* GtkIconView vfuncs */ +static void gtk_icon_view_real_select_all (GtkIconView *icon_view); +static void gtk_icon_view_real_unselect_all (GtkIconView *icon_view); +static void gtk_icon_view_real_select_cursor_item (GtkIconView *icon_view); +static void gtk_icon_view_real_toggle_cursor_item (GtkIconView *icon_view); +static gboolean gtk_icon_view_real_activate_cursor_item (GtkIconView *icon_view); + + /* Internal functions */ +static void gtk_icon_view_set_hadjustment_values (GtkIconView *icon_view); +static void gtk_icon_view_set_vadjustment_values (GtkIconView *icon_view); +static void gtk_icon_view_set_hadjustment (GtkIconView *icon_view, + GtkAdjustment *adjustment); +static void gtk_icon_view_set_vadjustment (GtkIconView *icon_view, + GtkAdjustment *adjustment); +static void gtk_icon_view_adjustment_changed (GtkAdjustment *adjustment, + GtkIconView *icon_view); +static void gtk_icon_view_layout (GtkIconView *icon_view); +static void gtk_icon_view_snapshot_item (GtkIconView *icon_view, + GtkSnapshot *snapshot, + GtkIconViewItem *item, + int x, + int y, + gboolean draw_focus); +static void gtk_icon_view_snapshot_rubberband (GtkIconView *icon_view, + GtkSnapshot *snapshot); +static void gtk_icon_view_queue_draw_path (GtkIconView *icon_view, + GtkTreePath *path); +static void gtk_icon_view_queue_draw_item (GtkIconView *icon_view, + GtkIconViewItem *item); +static void gtk_icon_view_start_rubberbanding (GtkIconView *icon_view, + GdkDevice *device, + int x, + int y); +static void gtk_icon_view_stop_rubberbanding (GtkIconView *icon_view); +static void gtk_icon_view_update_rubberband_selection (GtkIconView *icon_view); +static gboolean gtk_icon_view_item_hit_test (GtkIconView *icon_view, + GtkIconViewItem *item, + int x, + int y, + int width, + int height); +static gboolean gtk_icon_view_unselect_all_internal (GtkIconView *icon_view); +static void gtk_icon_view_update_rubberband (GtkIconView *icon_view); +static void gtk_icon_view_item_invalidate_size (GtkIconViewItem *item); +static void gtk_icon_view_invalidate_sizes (GtkIconView *icon_view); +static void gtk_icon_view_add_move_binding (GtkWidgetClass *widget_class, + guint keyval, + guint modmask, + GtkMovementStep step, + int count); +static gboolean gtk_icon_view_real_move_cursor (GtkIconView *icon_view, + GtkMovementStep step, + int count, + gboolean extend, + gboolean modify); +static void gtk_icon_view_move_cursor_up_down (GtkIconView *icon_view, + int count); +static void gtk_icon_view_move_cursor_page_up_down (GtkIconView *icon_view, + int count); +static void gtk_icon_view_move_cursor_left_right (GtkIconView *icon_view, + int count); +static void gtk_icon_view_move_cursor_start_end (GtkIconView *icon_view, + int count); +static void gtk_icon_view_scroll_to_item (GtkIconView *icon_view, + GtkIconViewItem *item); +static gboolean gtk_icon_view_select_all_between (GtkIconView *icon_view, + GtkIconViewItem *anchor, + GtkIconViewItem *cursor); + +static void gtk_icon_view_ensure_cell_area (GtkIconView *icon_view, + GtkCellArea *cell_area); + +static GtkCellArea *gtk_icon_view_cell_layout_get_area (GtkCellLayout *layout); + +static void gtk_icon_view_add_editable (GtkCellArea *area, + GtkCellRenderer *renderer, + GtkCellEditable *editable, + GdkRectangle *cell_area, + const char *path, + GtkIconView *icon_view); +static void gtk_icon_view_remove_editable (GtkCellArea *area, + GtkCellRenderer *renderer, + GtkCellEditable *editable, + GtkIconView *icon_view); +static void update_text_cell (GtkIconView *icon_view); +static void update_pixbuf_cell (GtkIconView *icon_view); + +/* Source side drag signals */ +static void gtk_icon_view_dnd_finished_cb (GdkDrag *drag, + GtkWidget *widget); +static GdkContentProvider * gtk_icon_view_drag_data_get (GtkIconView *icon_view, + GtkTreePath *source_row); + +/* Target side drag signals */ +static void gtk_icon_view_drag_leave (GtkDropTargetAsync *dest, + GdkDrop *drop, + GtkIconView *icon_view); +static GdkDragAction gtk_icon_view_drag_motion (GtkDropTargetAsync *dest, + GdkDrop *drop, + double x, + double y, + GtkIconView *icon_view); +static gboolean gtk_icon_view_drag_drop (GtkDropTargetAsync *dest, + GdkDrop *drop, + double x, + double y, + GtkIconView *icon_view); +static void gtk_icon_view_drag_data_received (GObject *source, + GAsyncResult *result, + gpointer data); +static gboolean gtk_icon_view_maybe_begin_drag (GtkIconView *icon_view, + double x, + double y, + GdkDevice *device); + +static void remove_scroll_timeout (GtkIconView *icon_view); + +/* GtkBuildable */ +static GtkBuildableIface *parent_buildable_iface; +static void gtk_icon_view_buildable_init (GtkBuildableIface *iface); +static gboolean gtk_icon_view_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const char *tagname, + GtkBuildableParser *parser, + gpointer *data); +static void gtk_icon_view_buildable_custom_tag_end (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const char *tagname, + gpointer data); + + +static guint icon_view_signals[LAST_SIGNAL] = { 0 }; + +G_DEFINE_TYPE_WITH_CODE (GtkIconView, gtk_icon_view, GTK_TYPE_WIDGET, + G_ADD_PRIVATE (GtkIconView) + G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT, + gtk_icon_view_cell_layout_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, + gtk_icon_view_buildable_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL)) + +static void +gtk_icon_view_class_init (GtkIconViewClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + gobject_class->constructed = gtk_icon_view_constructed; + gobject_class->dispose = gtk_icon_view_dispose; + gobject_class->set_property = gtk_icon_view_set_property; + gobject_class->get_property = gtk_icon_view_get_property; + + widget_class->get_request_mode = gtk_icon_view_get_request_mode; + widget_class->measure = gtk_icon_view_measure; + widget_class->size_allocate = gtk_icon_view_size_allocate; + widget_class->snapshot = gtk_icon_view_snapshot; + widget_class->focus = gtk_widget_focus_self; + widget_class->grab_focus = gtk_widget_grab_focus_self; + + klass->select_all = gtk_icon_view_real_select_all; + klass->unselect_all = gtk_icon_view_real_unselect_all; + klass->select_cursor_item = gtk_icon_view_real_select_cursor_item; + klass->toggle_cursor_item = gtk_icon_view_real_toggle_cursor_item; + klass->activate_cursor_item = gtk_icon_view_real_activate_cursor_item; + klass->move_cursor = gtk_icon_view_real_move_cursor; + + /* Properties */ + /** + * GtkIconView:selection-mode: + * + * The ::selection-mode property specifies the selection mode of + * icon view. If the mode is %GTK_SELECTION_MULTIPLE, rubberband selection + * is enabled, for the other modes, only keyboard selection is possible. + */ + g_object_class_install_property (gobject_class, + PROP_SELECTION_MODE, + g_param_spec_enum ("selection-mode", NULL, NULL, + GTK_TYPE_SELECTION_MODE, + GTK_SELECTION_SINGLE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + /** + * GtkIconView:pixbuf-column: + * + * The ::pixbuf-column property contains the number of the model column + * containing the pixbufs which are displayed. The pixbuf column must be + * of type `GDK_TYPE_PIXBUF`. Setting this property to -1 turns off the + * display of pixbufs. + */ + g_object_class_install_property (gobject_class, + PROP_PIXBUF_COLUMN, + g_param_spec_int ("pixbuf-column", NULL, NULL, + -1, G_MAXINT, -1, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + /** + * GtkIconView:text-column: + * + * The ::text-column property contains the number of the model column + * containing the texts which are displayed. The text column must be + * of type `G_TYPE_STRING`. If this property and the :markup-column + * property are both set to -1, no texts are displayed. + */ + g_object_class_install_property (gobject_class, + PROP_TEXT_COLUMN, + g_param_spec_int ("text-column", NULL, NULL, + -1, G_MAXINT, -1, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + + /** + * GtkIconView:markup-column: + * + * The ::markup-column property contains the number of the model column + * containing markup information to be displayed. The markup column must be + * of type `G_TYPE_STRING`. If this property and the :text-column property + * are both set to column numbers, it overrides the text column. + * If both are set to -1, no texts are displayed. + */ + g_object_class_install_property (gobject_class, + PROP_MARKUP_COLUMN, + g_param_spec_int ("markup-column", NULL, NULL, + -1, G_MAXINT, -1, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + g_object_class_install_property (gobject_class, + PROP_MODEL, + g_param_spec_object ("model", NULL, NULL, + GTK_TYPE_TREE_MODEL, + GTK_PARAM_READWRITE)); + + /** + * GtkIconView:columns: + * + * The columns property contains the number of the columns in which the + * items should be displayed. If it is -1, the number of columns will + * be chosen automatically to fill the available area. + */ + g_object_class_install_property (gobject_class, + PROP_COLUMNS, + g_param_spec_int ("columns", NULL, NULL, + -1, G_MAXINT, -1, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + + /** + * GtkIconView:item-width: + * + * The item-width property specifies the width to use for each item. + * If it is set to -1, the icon view will automatically determine a + * suitable item size. + */ + g_object_class_install_property (gobject_class, + PROP_ITEM_WIDTH, + g_param_spec_int ("item-width", NULL, NULL, + -1, G_MAXINT, -1, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + /** + * GtkIconView:spacing: + * + * The spacing property specifies the space which is inserted between + * the cells (i.e. the icon and the text) of an item. + */ + g_object_class_install_property (gobject_class, + PROP_SPACING, + g_param_spec_int ("spacing", NULL, NULL, + 0, G_MAXINT, 0, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + /** + * GtkIconView:row-spacing: + * + * The row-spacing property specifies the space which is inserted between + * the rows of the icon view. + */ + g_object_class_install_property (gobject_class, + PROP_ROW_SPACING, + g_param_spec_int ("row-spacing", NULL, NULL, + 0, G_MAXINT, 6, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + /** + * GtkIconView:column-spacing: + * + * The column-spacing property specifies the space which is inserted between + * the columns of the icon view. + */ + g_object_class_install_property (gobject_class, + PROP_COLUMN_SPACING, + g_param_spec_int ("column-spacing", NULL, NULL, + 0, G_MAXINT, 6, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + /** + * GtkIconView:margin: + * + * The margin property specifies the space which is inserted + * at the edges of the icon view. + */ + g_object_class_install_property (gobject_class, + PROP_MARGIN, + g_param_spec_int ("margin", NULL, NULL, + 0, G_MAXINT, 6, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + /** + * GtkIconView:item-orientation: + * + * The item-orientation property specifies how the cells (i.e. the icon and + * the text) of the item are positioned relative to each other. + */ + g_object_class_install_property (gobject_class, + PROP_ITEM_ORIENTATION, + g_param_spec_enum ("item-orientation", NULL, NULL, + GTK_TYPE_ORIENTATION, + GTK_ORIENTATION_VERTICAL, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + /** + * GtkIconView:reorderable: + * + * The reorderable property specifies if the items can be reordered + * by DND. + */ + g_object_class_install_property (gobject_class, + PROP_REORDERABLE, + g_param_spec_boolean ("reorderable", NULL, NULL, + FALSE, + G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + g_object_class_install_property (gobject_class, + PROP_TOOLTIP_COLUMN, + g_param_spec_int ("tooltip-column", NULL, NULL, + -1, + G_MAXINT, + -1, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + /** + * GtkIconView:item-padding: + * + * The item-padding property specifies the padding around each + * of the icon view's item. + */ + g_object_class_install_property (gobject_class, + PROP_ITEM_PADDING, + g_param_spec_int ("item-padding", NULL, NULL, + 0, G_MAXINT, 6, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + /** + * GtkIconView:cell-area: + * + * The `GtkCellArea` used to layout cell renderers for this view. + * + * If no area is specified when creating the icon view with gtk_icon_view_new_with_area() + * a `GtkCellAreaBox` will be used. + */ + g_object_class_install_property (gobject_class, + PROP_CELL_AREA, + g_param_spec_object ("cell-area", NULL, NULL, + GTK_TYPE_CELL_AREA, + GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + /** + * GtkIconView:activate-on-single-click: + * + * The activate-on-single-click property specifies whether the "item-activated" signal + * will be emitted after a single click. + */ + g_object_class_install_property (gobject_class, + PROP_ACTIVATE_ON_SINGLE_CLICK, + g_param_spec_boolean ("activate-on-single-click", NULL, NULL, + FALSE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + /* Scrollable interface properties */ + g_object_class_override_property (gobject_class, PROP_HADJUSTMENT, "hadjustment"); + g_object_class_override_property (gobject_class, PROP_VADJUSTMENT, "vadjustment"); + g_object_class_override_property (gobject_class, PROP_HSCROLL_POLICY, "hscroll-policy"); + g_object_class_override_property (gobject_class, PROP_VSCROLL_POLICY, "vscroll-policy"); + + /* Signals */ + /** + * GtkIconView::item-activated: + * @iconview: the object on which the signal is emitted + * @path: the `GtkTreePath` for the activated item + * + * The ::item-activated signal is emitted when the method + * gtk_icon_view_item_activated() is called, when the user double + * clicks an item with the "activate-on-single-click" property set + * to %FALSE, or when the user single clicks an item when the + * "activate-on-single-click" property set to %TRUE. It is also + * emitted when a non-editable item is selected and one of the keys: + * Space, Return or Enter is pressed. + */ + icon_view_signals[ITEM_ACTIVATED] = + g_signal_new (I_("item-activated"), + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkIconViewClass, item_activated), + NULL, NULL, + NULL, + G_TYPE_NONE, 1, + GTK_TYPE_TREE_PATH); + + /** + * GtkIconView::selection-changed: + * @iconview: the object on which the signal is emitted + * + * The ::selection-changed signal is emitted when the selection + * (i.e. the set of selected items) changes. + */ + icon_view_signals[SELECTION_CHANGED] = + g_signal_new (I_("selection-changed"), + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GtkIconViewClass, selection_changed), + NULL, NULL, + NULL, + G_TYPE_NONE, 0); + + /** + * GtkIconView::select-all: + * @iconview: the object on which the signal is emitted + * + * A [keybinding signal][class@Gtk.SignalAction] + * which gets emitted when the user selects all items. + * + * Applications should not connect to it, but may emit it with + * g_signal_emit_by_name() if they need to control selection + * programmatically. + * + * The default binding for this signal is Ctrl-a. + */ + icon_view_signals[SELECT_ALL] = + g_signal_new (I_("select-all"), + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkIconViewClass, select_all), + NULL, NULL, + NULL, + G_TYPE_NONE, 0); + + /** + * GtkIconView::unselect-all: + * @iconview: the object on which the signal is emitted + * + * A [keybinding signal][class@Gtk.SignalAction] + * which gets emitted when the user unselects all items. + * + * Applications should not connect to it, but may emit it with + * g_signal_emit_by_name() if they need to control selection + * programmatically. + * + * The default binding for this signal is Ctrl-Shift-a. + */ + icon_view_signals[UNSELECT_ALL] = + g_signal_new (I_("unselect-all"), + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkIconViewClass, unselect_all), + NULL, NULL, + NULL, + G_TYPE_NONE, 0); + + /** + * GtkIconView::select-cursor-item: + * @iconview: the object on which the signal is emitted + * + * A [keybinding signal][class@Gtk.SignalAction] + * which gets emitted when the user selects the item that is currently + * focused. + * + * Applications should not connect to it, but may emit it with + * g_signal_emit_by_name() if they need to control selection + * programmatically. + * + * There is no default binding for this signal. + */ + icon_view_signals[SELECT_CURSOR_ITEM] = + g_signal_new (I_("select-cursor-item"), + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkIconViewClass, select_cursor_item), + NULL, NULL, + NULL, + G_TYPE_NONE, 0); + + /** + * GtkIconView::toggle-cursor-item: + * @iconview: the object on which the signal is emitted + * + * A [keybinding signal][class@Gtk.SignalAction] + * which gets emitted when the user toggles whether the currently + * focused item is selected or not. The exact effect of this + * depend on the selection mode. + * + * Applications should not connect to it, but may emit it with + * g_signal_emit_by_name() if they need to control selection + * programmatically. + * + * There is no default binding for this signal is Ctrl-Space. + */ + icon_view_signals[TOGGLE_CURSOR_ITEM] = + g_signal_new (I_("toggle-cursor-item"), + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkIconViewClass, toggle_cursor_item), + NULL, NULL, + NULL, + G_TYPE_NONE, 0); + + /** + * GtkIconView::activate-cursor-item: + * @iconview: the object on which the signal is emitted + * + * A [keybinding signal][class@Gtk.SignalAction] + * which gets emitted when the user activates the currently + * focused item. + * + * Applications should not connect to it, but may emit it with + * g_signal_emit_by_name() if they need to control activation + * programmatically. + * + * The default bindings for this signal are Space, Return and Enter. + */ + icon_view_signals[ACTIVATE_CURSOR_ITEM] = + g_signal_new (I_("activate-cursor-item"), + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkIconViewClass, activate_cursor_item), + NULL, NULL, + _gtk_marshal_BOOLEAN__VOID, + G_TYPE_BOOLEAN, 0); + g_signal_set_va_marshaller (icon_view_signals[ACTIVATE_CURSOR_ITEM], + G_TYPE_FROM_CLASS (klass), + _gtk_marshal_BOOLEAN__VOIDv); + + /** + * GtkIconView::move-cursor: + * @iconview: the object which received the signal + * @step: the granularity of the move, as a `GtkMovementStep` + * @count: the number of @step units to move + * @extend: whether to extend the selection + * @modify: whether to modify the selection + * + * The ::move-cursor signal is a + * [keybinding signal][class@Gtk.SignalAction] + * which gets emitted when the user initiates a cursor movement. + * + * Applications should not connect to it, but may emit it with + * g_signal_emit_by_name() if they need to control the cursor + * programmatically. + * + * The default bindings for this signal include + * - Arrow keys which move by individual steps + * - Home/End keys which move to the first/last item + * - PageUp/PageDown which move by "pages" + * All of these will extend the selection when combined with + * the Shift modifier. + */ + icon_view_signals[MOVE_CURSOR] = + g_signal_new (I_("move-cursor"), + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkIconViewClass, move_cursor), + NULL, NULL, + _gtk_marshal_BOOLEAN__ENUM_INT_BOOLEAN_BOOLEAN, + G_TYPE_BOOLEAN, 4, + GTK_TYPE_MOVEMENT_STEP, + G_TYPE_INT, + G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN); + g_signal_set_va_marshaller (icon_view_signals[MOVE_CURSOR], + G_TYPE_FROM_CLASS (klass), + _gtk_marshal_BOOLEAN__ENUM_INT_BOOLEAN_BOOLEANv); + + /* Key bindings */ + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_a, GDK_CONTROL_MASK, + "select-all", + NULL); + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_a, GDK_CONTROL_MASK | GDK_SHIFT_MASK, + "unselect-all", + NULL); + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_space, GDK_CONTROL_MASK, + "toggle-cursor-item", + NULL); + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_KP_Space, GDK_CONTROL_MASK, + "toggle-cursor-item", + NULL); + + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_space, 0, + "activate-cursor-item", + NULL); + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_KP_Space, 0, + "activate-cursor-item", + NULL); + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_Return, 0, + "activate-cursor-item", + NULL); + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_ISO_Enter, 0, + "activate-cursor-item", + NULL); + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_KP_Enter, 0, + "activate-cursor-item", + NULL); + + gtk_icon_view_add_move_binding (widget_class, GDK_KEY_Up, 0, + GTK_MOVEMENT_DISPLAY_LINES, -1); + gtk_icon_view_add_move_binding (widget_class, GDK_KEY_KP_Up, 0, + GTK_MOVEMENT_DISPLAY_LINES, -1); + + gtk_icon_view_add_move_binding (widget_class, GDK_KEY_Down, 0, + GTK_MOVEMENT_DISPLAY_LINES, 1); + gtk_icon_view_add_move_binding (widget_class, GDK_KEY_KP_Down, 0, + GTK_MOVEMENT_DISPLAY_LINES, 1); + + gtk_icon_view_add_move_binding (widget_class, GDK_KEY_p, GDK_CONTROL_MASK, + GTK_MOVEMENT_DISPLAY_LINES, -1); + + gtk_icon_view_add_move_binding (widget_class, GDK_KEY_n, GDK_CONTROL_MASK, + GTK_MOVEMENT_DISPLAY_LINES, 1); + + gtk_icon_view_add_move_binding (widget_class, GDK_KEY_Home, 0, + GTK_MOVEMENT_BUFFER_ENDS, -1); + gtk_icon_view_add_move_binding (widget_class, GDK_KEY_KP_Home, 0, + GTK_MOVEMENT_BUFFER_ENDS, -1); + + gtk_icon_view_add_move_binding (widget_class, GDK_KEY_End, 0, + GTK_MOVEMENT_BUFFER_ENDS, 1); + gtk_icon_view_add_move_binding (widget_class, GDK_KEY_KP_End, 0, + GTK_MOVEMENT_BUFFER_ENDS, 1); + + gtk_icon_view_add_move_binding (widget_class, GDK_KEY_Page_Up, 0, + GTK_MOVEMENT_PAGES, -1); + gtk_icon_view_add_move_binding (widget_class, GDK_KEY_KP_Page_Up, 0, + GTK_MOVEMENT_PAGES, -1); + + gtk_icon_view_add_move_binding (widget_class, GDK_KEY_Page_Down, 0, + GTK_MOVEMENT_PAGES, 1); + gtk_icon_view_add_move_binding (widget_class, GDK_KEY_KP_Page_Down, 0, + GTK_MOVEMENT_PAGES, 1); + + gtk_icon_view_add_move_binding (widget_class, GDK_KEY_Right, 0, + GTK_MOVEMENT_VISUAL_POSITIONS, 1); + gtk_icon_view_add_move_binding (widget_class, GDK_KEY_Left, 0, + GTK_MOVEMENT_VISUAL_POSITIONS, -1); + + gtk_icon_view_add_move_binding (widget_class, GDK_KEY_KP_Right, 0, + GTK_MOVEMENT_VISUAL_POSITIONS, 1); + gtk_icon_view_add_move_binding (widget_class, GDK_KEY_KP_Left, 0, + GTK_MOVEMENT_VISUAL_POSITIONS, -1); + + gtk_widget_class_set_css_name (widget_class, I_("iconview")); +} + +static void +gtk_icon_view_buildable_add_child (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const char *type) +{ + if (GTK_IS_CELL_RENDERER (child)) + _gtk_cell_layout_buildable_add_child (buildable, builder, child, type); + else + parent_buildable_iface->add_child (buildable, builder, child, type); +} + +static void +gtk_icon_view_buildable_init (GtkBuildableIface *iface) +{ + parent_buildable_iface = g_type_interface_peek_parent (iface); + iface->add_child = gtk_icon_view_buildable_add_child; + iface->custom_tag_start = gtk_icon_view_buildable_custom_tag_start; + iface->custom_tag_end = gtk_icon_view_buildable_custom_tag_end; +} + +static void +gtk_icon_view_cell_layout_init (GtkCellLayoutIface *iface) +{ + iface->get_area = gtk_icon_view_cell_layout_get_area; +} + +static void +gtk_icon_view_init (GtkIconView *icon_view) +{ + GtkEventController *controller; + GtkGesture *gesture; + + icon_view->priv = gtk_icon_view_get_instance_private (icon_view); + + icon_view->priv->width = 0; + icon_view->priv->height = 0; + icon_view->priv->selection_mode = GTK_SELECTION_SINGLE; + icon_view->priv->pressed_button = -1; + icon_view->priv->press_start_x = -1; + icon_view->priv->press_start_y = -1; + icon_view->priv->text_column = -1; + icon_view->priv->markup_column = -1; + icon_view->priv->pixbuf_column = -1; + icon_view->priv->text_cell = NULL; + icon_view->priv->pixbuf_cell = NULL; + icon_view->priv->tooltip_column = -1; + icon_view->priv->mouse_x = -1; + icon_view->priv->mouse_y = -1; + + gtk_widget_set_overflow (GTK_WIDGET (icon_view), GTK_OVERFLOW_HIDDEN); + gtk_widget_set_focusable (GTK_WIDGET (icon_view), TRUE); + + icon_view->priv->item_orientation = GTK_ORIENTATION_VERTICAL; + + icon_view->priv->columns = -1; + icon_view->priv->item_width = -1; + icon_view->priv->spacing = 0; + icon_view->priv->row_spacing = 6; + icon_view->priv->column_spacing = 6; + icon_view->priv->margin = 6; + icon_view->priv->item_padding = 6; + icon_view->priv->activate_on_single_click = FALSE; + + icon_view->priv->draw_focus = TRUE; + + icon_view->priv->row_contexts = + g_ptr_array_new_with_free_func ((GDestroyNotify)g_object_unref); + + gtk_widget_add_css_class (GTK_WIDGET (icon_view), "view"); + + gesture = gtk_gesture_click_new (); + g_signal_connect (gesture, "pressed", G_CALLBACK (gtk_icon_view_button_press), + icon_view); + g_signal_connect (gesture, "released", G_CALLBACK (gtk_icon_view_button_release), + icon_view); + gtk_widget_add_controller (GTK_WIDGET (icon_view), GTK_EVENT_CONTROLLER (gesture)); + + controller = gtk_event_controller_motion_new (); + g_signal_connect (controller, "leave", G_CALLBACK (gtk_icon_view_leave), icon_view); + g_signal_connect (controller, "motion", G_CALLBACK (gtk_icon_view_motion), icon_view); + gtk_widget_add_controller (GTK_WIDGET (icon_view), controller); + + controller = gtk_event_controller_key_new (); + g_signal_connect (controller, "key-pressed", G_CALLBACK (gtk_icon_view_key_pressed), + icon_view); + gtk_widget_add_controller (GTK_WIDGET (icon_view), controller); +} + +/* GObject methods */ + +static void +gtk_icon_view_constructed (GObject *object) +{ + GtkIconView *icon_view = GTK_ICON_VIEW (object); + + G_OBJECT_CLASS (gtk_icon_view_parent_class)->constructed (object); + + gtk_icon_view_ensure_cell_area (icon_view, NULL); +} + +static void +gtk_icon_view_dispose (GObject *object) +{ + GtkIconView *icon_view; + GtkIconViewPrivate *priv; + + icon_view = GTK_ICON_VIEW (object); + priv = icon_view->priv; + + gtk_icon_view_set_model (icon_view, NULL); + + if (icon_view->priv->scroll_to_path != NULL) + { + gtk_tree_row_reference_free (icon_view->priv->scroll_to_path); + icon_view->priv->scroll_to_path = NULL; + } + + remove_scroll_timeout (icon_view); + + if (icon_view->priv->hadjustment != NULL) + { + g_object_unref (icon_view->priv->hadjustment); + icon_view->priv->hadjustment = NULL; + } + + if (icon_view->priv->vadjustment != NULL) + { + g_object_unref (icon_view->priv->vadjustment); + icon_view->priv->vadjustment = NULL; + } + + if (priv->cell_area_context) + { + g_object_unref (priv->cell_area_context); + priv->cell_area_context = NULL; + } + + if (priv->row_contexts) + { + g_ptr_array_free (priv->row_contexts, TRUE); + priv->row_contexts = NULL; + } + + if (priv->cell_area) + { + gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); + + g_signal_handler_disconnect (priv->cell_area, priv->add_editable_id); + g_signal_handler_disconnect (priv->cell_area, priv->remove_editable_id); + priv->add_editable_id = 0; + priv->remove_editable_id = 0; + + g_object_unref (priv->cell_area); + priv->cell_area = NULL; + } + + g_clear_object (&priv->key_controller); + + g_clear_pointer (&priv->source_formats, gdk_content_formats_unref); + + G_OBJECT_CLASS (gtk_icon_view_parent_class)->dispose (object); +} + +static void +gtk_icon_view_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkIconView *icon_view; + GtkCellArea *area; + + icon_view = GTK_ICON_VIEW (object); + + switch (prop_id) + { + case PROP_SELECTION_MODE: + gtk_icon_view_set_selection_mode (icon_view, g_value_get_enum (value)); + break; + case PROP_PIXBUF_COLUMN: + gtk_icon_view_set_pixbuf_column (icon_view, g_value_get_int (value)); + break; + case PROP_TEXT_COLUMN: + gtk_icon_view_set_text_column (icon_view, g_value_get_int (value)); + break; + case PROP_MARKUP_COLUMN: + gtk_icon_view_set_markup_column (icon_view, g_value_get_int (value)); + break; + case PROP_MODEL: + gtk_icon_view_set_model (icon_view, g_value_get_object (value)); + break; + case PROP_ITEM_ORIENTATION: + gtk_icon_view_set_item_orientation (icon_view, g_value_get_enum (value)); + break; + case PROP_COLUMNS: + gtk_icon_view_set_columns (icon_view, g_value_get_int (value)); + break; + case PROP_ITEM_WIDTH: + gtk_icon_view_set_item_width (icon_view, g_value_get_int (value)); + break; + case PROP_SPACING: + gtk_icon_view_set_spacing (icon_view, g_value_get_int (value)); + break; + case PROP_ROW_SPACING: + gtk_icon_view_set_row_spacing (icon_view, g_value_get_int (value)); + break; + case PROP_COLUMN_SPACING: + gtk_icon_view_set_column_spacing (icon_view, g_value_get_int (value)); + break; + case PROP_MARGIN: + gtk_icon_view_set_margin (icon_view, g_value_get_int (value)); + break; + case PROP_REORDERABLE: + gtk_icon_view_set_reorderable (icon_view, g_value_get_boolean (value)); + break; + + case PROP_TOOLTIP_COLUMN: + gtk_icon_view_set_tooltip_column (icon_view, g_value_get_int (value)); + break; + + case PROP_ITEM_PADDING: + gtk_icon_view_set_item_padding (icon_view, g_value_get_int (value)); + break; + + case PROP_ACTIVATE_ON_SINGLE_CLICK: + gtk_icon_view_set_activate_on_single_click (icon_view, g_value_get_boolean (value)); + break; + + case PROP_CELL_AREA: + /* Construct-only, can only be assigned once */ + area = g_value_get_object (value); + if (area) + { + if (icon_view->priv->cell_area != NULL) + { + g_warning ("cell-area has already been set, ignoring construct property"); + g_object_ref_sink (area); + g_object_unref (area); + } + else + gtk_icon_view_ensure_cell_area (icon_view, area); + } + break; + + case PROP_HADJUSTMENT: + gtk_icon_view_set_hadjustment (icon_view, g_value_get_object (value)); + break; + case PROP_VADJUSTMENT: + gtk_icon_view_set_vadjustment (icon_view, g_value_get_object (value)); + break; + case PROP_HSCROLL_POLICY: + if (icon_view->priv->hscroll_policy != g_value_get_enum (value)) + { + icon_view->priv->hscroll_policy = g_value_get_enum (value); + gtk_widget_queue_resize (GTK_WIDGET (icon_view)); + g_object_notify_by_pspec (object, pspec); + } + break; + case PROP_VSCROLL_POLICY: + if (icon_view->priv->vscroll_policy != g_value_get_enum (value)) + { + icon_view->priv->vscroll_policy = g_value_get_enum (value); + gtk_widget_queue_resize (GTK_WIDGET (icon_view)); + g_object_notify_by_pspec (object, pspec); + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_icon_view_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkIconView *icon_view; + + icon_view = GTK_ICON_VIEW (object); + + switch (prop_id) + { + case PROP_SELECTION_MODE: + g_value_set_enum (value, icon_view->priv->selection_mode); + break; + case PROP_PIXBUF_COLUMN: + g_value_set_int (value, icon_view->priv->pixbuf_column); + break; + case PROP_TEXT_COLUMN: + g_value_set_int (value, icon_view->priv->text_column); + break; + case PROP_MARKUP_COLUMN: + g_value_set_int (value, icon_view->priv->markup_column); + break; + case PROP_MODEL: + g_value_set_object (value, icon_view->priv->model); + break; + case PROP_ITEM_ORIENTATION: + g_value_set_enum (value, icon_view->priv->item_orientation); + break; + case PROP_COLUMNS: + g_value_set_int (value, icon_view->priv->columns); + break; + case PROP_ITEM_WIDTH: + g_value_set_int (value, icon_view->priv->item_width); + break; + case PROP_SPACING: + g_value_set_int (value, icon_view->priv->spacing); + break; + case PROP_ROW_SPACING: + g_value_set_int (value, icon_view->priv->row_spacing); + break; + case PROP_COLUMN_SPACING: + g_value_set_int (value, icon_view->priv->column_spacing); + break; + case PROP_MARGIN: + g_value_set_int (value, icon_view->priv->margin); + break; + case PROP_REORDERABLE: + g_value_set_boolean (value, icon_view->priv->reorderable); + break; + case PROP_TOOLTIP_COLUMN: + g_value_set_int (value, icon_view->priv->tooltip_column); + break; + + case PROP_ITEM_PADDING: + g_value_set_int (value, icon_view->priv->item_padding); + break; + + case PROP_ACTIVATE_ON_SINGLE_CLICK: + g_value_set_boolean (value, icon_view->priv->activate_on_single_click); + break; + + case PROP_CELL_AREA: + g_value_set_object (value, icon_view->priv->cell_area); + break; + + case PROP_HADJUSTMENT: + g_value_set_object (value, icon_view->priv->hadjustment); + break; + case PROP_VADJUSTMENT: + g_value_set_object (value, icon_view->priv->vadjustment); + break; + case PROP_HSCROLL_POLICY: + g_value_set_enum (value, icon_view->priv->hscroll_policy); + break; + case PROP_VSCROLL_POLICY: + g_value_set_enum (value, icon_view->priv->vscroll_policy); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +/* GtkWidget methods */ + +static int +gtk_icon_view_get_n_items (GtkIconView *icon_view) +{ + GtkIconViewPrivate *priv = icon_view->priv; + + if (priv->model == NULL) + return 0; + + return gtk_tree_model_iter_n_children (priv->model, NULL); +} + +static void +adjust_wrap_width (GtkIconView *icon_view) +{ + if (icon_view->priv->text_cell) + { + int pixbuf_width, wrap_width; + + if (icon_view->priv->items && icon_view->priv->pixbuf_cell) + { + gtk_cell_renderer_get_preferred_width (icon_view->priv->pixbuf_cell, + GTK_WIDGET (icon_view), + &pixbuf_width, NULL); + } + else + { + pixbuf_width = 0; + } + + if (icon_view->priv->item_width >= 0) + { + if (icon_view->priv->item_orientation == GTK_ORIENTATION_VERTICAL) + { + wrap_width = icon_view->priv->item_width; + } + else + { + wrap_width = icon_view->priv->item_width - pixbuf_width; + } + + wrap_width -= 2 * icon_view->priv->item_padding * 2; + } + else + { + wrap_width = MAX (pixbuf_width * 2, 50); + } + + if (icon_view->priv->items && icon_view->priv->pixbuf_cell) + { + /* Here we go with the same old guess, try the icon size and set double + * the size of the first icon found in the list, naive but works much + * of the time */ + + wrap_width = MAX (wrap_width * 2, 50); + } + + g_object_set (icon_view->priv->text_cell, "wrap-width", wrap_width, NULL); + g_object_set (icon_view->priv->text_cell, "width", wrap_width, NULL); + } +} + +/* General notes about layout + * + * The icon view is layouted like this: + * + * +----------+ s +----------+ + * | padding | p | padding | + * | +------+ | a | +------+ | + * | | cell | | c | | cell | | + * | +------+ | i | +------+ | + * | | n | | + * +----------+ g +----------+ + * + * In size request and allocation code, there are 3 sizes that are used: + * * cell size + * This is the size returned by gtk_cell_area_get_preferred_foo(). In places + * where code is interacting with the cell area and renderers this is useful. + * * padded size + * This is the cell size plus the item padding on each side. + * * spaced size + * This is the padded size plus the spacing. This is what’s used for most + * calculations because it can (ab)use the following formula: + * iconview_size = 2 * margin + n_items * spaced_size - spacing + * So when reading this code and fixing my bugs where I confuse these two, be + * aware of this distinction. + */ +static void +cell_area_get_preferred_size (GtkIconView *icon_view, + GtkCellAreaContext *context, + GtkOrientation orientation, + int for_size, + int *minimum, + int *natural) +{ + if (orientation == GTK_ORIENTATION_HORIZONTAL) + { + if (for_size > 0) + gtk_cell_area_get_preferred_width_for_height (icon_view->priv->cell_area, + context, + GTK_WIDGET (icon_view), + for_size, + minimum, natural); + else + gtk_cell_area_get_preferred_width (icon_view->priv->cell_area, + context, + GTK_WIDGET (icon_view), + minimum, natural); + } + else + { + if (for_size > 0) + gtk_cell_area_get_preferred_height_for_width (icon_view->priv->cell_area, + context, + GTK_WIDGET (icon_view), + for_size, + minimum, natural); + else + gtk_cell_area_get_preferred_height (icon_view->priv->cell_area, + context, + GTK_WIDGET (icon_view), + minimum, natural); + } +} + +static gboolean +gtk_icon_view_is_empty (GtkIconView *icon_view) +{ + return icon_view->priv->items == NULL; +} + +static void +gtk_icon_view_get_preferred_item_size (GtkIconView *icon_view, + GtkOrientation orientation, + int for_size, + int *minimum, + int *natural) +{ + GtkIconViewPrivate *priv = icon_view->priv; + GtkCellAreaContext *context; + GList *items; + + g_assert (!gtk_icon_view_is_empty (icon_view)); + + context = gtk_cell_area_create_context (priv->cell_area); + + for_size -= 2 * priv->item_padding; + + if (for_size > 0) + { + /* This is necessary for the context to work properly */ + for (items = priv->items; items; items = items->next) + { + GtkIconViewItem *item = items->data; + + _gtk_icon_view_set_cell_data (icon_view, item); + cell_area_get_preferred_size (icon_view, context, 1 - orientation, -1, NULL, NULL); + } + } + + for (items = priv->items; items; items = items->next) + { + GtkIconViewItem *item = items->data; + + _gtk_icon_view_set_cell_data (icon_view, item); + if (items == priv->items) + adjust_wrap_width (icon_view); + cell_area_get_preferred_size (icon_view, context, orientation, for_size, NULL, NULL); + } + + if (orientation == GTK_ORIENTATION_HORIZONTAL) + { + if (for_size > 0) + gtk_cell_area_context_get_preferred_width_for_height (context, + for_size, + minimum, natural); + else + gtk_cell_area_context_get_preferred_width (context, + minimum, natural); + } + else + { + if (for_size > 0) + gtk_cell_area_context_get_preferred_height_for_width (context, + for_size, + minimum, natural); + else + gtk_cell_area_context_get_preferred_height (context, + minimum, natural); + } + + if (orientation == GTK_ORIENTATION_HORIZONTAL && priv->item_width >= 0) + { + if (minimum) + *minimum = MAX (*minimum, priv->item_width); + if (natural) + *natural = *minimum; + } + + if (minimum) + *minimum = MAX (1, *minimum + 2 * priv->item_padding); + if (natural) + *natural = MAX (1, *natural + 2 * priv->item_padding); + + g_object_unref (context); +} + +static void +gtk_icon_view_compute_n_items_for_size (GtkIconView *icon_view, + GtkOrientation orientation, + int size, + int *min_items, + int *min_item_size, + int *max_items, + int *max_item_size) +{ + GtkIconViewPrivate *priv = icon_view->priv; + int minimum, natural, spacing; + + g_return_if_fail (min_item_size == NULL || min_items != NULL); + g_return_if_fail (max_item_size == NULL || max_items != NULL); + g_return_if_fail (!gtk_icon_view_is_empty (icon_view)); + + gtk_icon_view_get_preferred_item_size (icon_view, orientation, -1, &minimum, &natural); + + if (orientation == GTK_ORIENTATION_HORIZONTAL) + spacing = priv->column_spacing; + else + spacing = priv->row_spacing; + + size -= 2 * priv->margin; + size += spacing; + minimum += spacing; + natural += spacing; + + if (priv->columns > 0) + { + if (orientation == GTK_ORIENTATION_HORIZONTAL) + { + if (min_items) + *min_items = priv->columns; + if (max_items) + *max_items = priv->columns; + } + else + { + int n_items = gtk_icon_view_get_n_items (icon_view); + + if (min_items) + *min_items = (n_items + priv->columns - 1) / priv->columns; + if (max_items) + *max_items = (n_items + priv->columns - 1) / priv->columns; + } + } + else + { + if (max_items) + { + if (size <= minimum) + *max_items = 1; + else + *max_items = size / minimum; + } + + if (min_items) + { + if (size <= natural) + *min_items = 1; + else + *min_items = size / natural; + } + } + + if (min_item_size) + { + *min_item_size = size / *min_items; + *min_item_size = CLAMP (*min_item_size, minimum, natural); + *min_item_size -= spacing; + *min_item_size -= 2 * priv->item_padding; + } + + if (max_item_size) + { + *max_item_size = size / *max_items; + *max_item_size = CLAMP (*max_item_size, minimum, natural); + *max_item_size -= spacing; + *max_item_size -= 2 * priv->item_padding; + } +} + +static GtkSizeRequestMode +gtk_icon_view_get_request_mode (GtkWidget *widget) +{ + return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH; +} + +static void +gtk_icon_view_measure (GtkWidget *widget, + GtkOrientation orientation, + int for_size, + int *minimum, + int *natural, + int *minimum_baseline, + int *natural_baseline) +{ + GtkIconView *icon_view = GTK_ICON_VIEW (widget); + GtkIconViewPrivate *priv = icon_view->priv; + GtkOrientation other_orientation = orientation == GTK_ORIENTATION_HORIZONTAL ? + GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL; + int item_min, item_nat, items = 0, item_size = 0, n_items; + + if (gtk_icon_view_is_empty (icon_view)) + { + *minimum = *natural = 2 * priv->margin; + return; + } + + n_items = gtk_icon_view_get_n_items (icon_view); + + if (for_size < 0) + { + gtk_icon_view_get_preferred_item_size (icon_view, orientation, -1, &item_min, &item_nat); + + if (priv->columns > 0) + { + int n_rows = (n_items + priv->columns - 1) / priv->columns; + + *minimum = item_min * n_rows + priv->row_spacing * (n_rows - 1); + *natural = item_nat * n_rows + priv->row_spacing * (n_rows - 1); + } + else + { + *minimum = item_min; + *natural = item_nat * n_items + priv->row_spacing * (n_items - 1); + } + } + else + { + gtk_icon_view_compute_n_items_for_size (icon_view, orientation, for_size, NULL, NULL, &items, &item_size); + gtk_icon_view_get_preferred_item_size (icon_view, other_orientation, item_size, &item_min, &item_nat); + *minimum = (item_min + priv->row_spacing) * ((n_items + items - 1) / items) - priv->row_spacing; + *natural = (item_nat + priv->row_spacing) * ((n_items + items - 1) / items) - priv->row_spacing; + } + + *minimum += 2 * priv->margin; + *natural += 2 * priv->margin; +} + + +static void +gtk_icon_view_allocate_children (GtkIconView *icon_view) +{ + GList *list; + + for (list = icon_view->priv->children; list; list = list->next) + { + GtkIconViewChild *child = list->data; + + /* totally ignore our child's requisition */ + gtk_widget_size_allocate (child->widget, &child->area, -1); + } +} + +static void +gtk_icon_view_size_allocate (GtkWidget *widget, + int width, + int height, + int baseline) +{ + GtkIconView *icon_view = GTK_ICON_VIEW (widget); + + gtk_icon_view_layout (icon_view); + + gtk_icon_view_allocate_children (icon_view); + + /* Delay signal emission */ + g_object_freeze_notify (G_OBJECT (icon_view->priv->hadjustment)); + g_object_freeze_notify (G_OBJECT (icon_view->priv->vadjustment)); + + gtk_icon_view_set_hadjustment_values (icon_view); + gtk_icon_view_set_vadjustment_values (icon_view); + + if (gtk_widget_get_realized (widget) && + icon_view->priv->scroll_to_path) + { + GtkTreePath *path; + path = gtk_tree_row_reference_get_path (icon_view->priv->scroll_to_path); + gtk_tree_row_reference_free (icon_view->priv->scroll_to_path); + icon_view->priv->scroll_to_path = NULL; + + gtk_icon_view_scroll_to_path (icon_view, path, + icon_view->priv->scroll_to_use_align, + icon_view->priv->scroll_to_row_align, + icon_view->priv->scroll_to_col_align); + gtk_tree_path_free (path); + } + + /* Emit any pending signals now */ + g_object_thaw_notify (G_OBJECT (icon_view->priv->hadjustment)); + g_object_thaw_notify (G_OBJECT (icon_view->priv->vadjustment)); +} + +static void +gtk_icon_view_snapshot (GtkWidget *widget, + GtkSnapshot *snapshot) +{ + GtkIconView *icon_view; + GList *icons; + GtkTreePath *path; + int dest_index; + GtkIconViewDropPosition dest_pos; + GtkIconViewItem *dest_item = NULL; + GtkStyleContext *context; + int width, height; + double offset_x, offset_y; + + icon_view = GTK_ICON_VIEW (widget); + + context = gtk_widget_get_style_context (widget); + width = gtk_widget_get_width (widget); + height = gtk_widget_get_height (widget); + + offset_x = gtk_adjustment_get_value (icon_view->priv->hadjustment); + offset_y = gtk_adjustment_get_value (icon_view->priv->vadjustment); + + gtk_snapshot_save (snapshot); + gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (- offset_x, - offset_y)); + + gtk_icon_view_get_drag_dest_item (icon_view, &path, &dest_pos); + + if (path) + { + dest_index = gtk_tree_path_get_indices (path)[0]; + gtk_tree_path_free (path); + } + else + dest_index = -1; + + for (icons = icon_view->priv->items; icons; icons = icons->next) + { + GtkIconViewItem *item = icons->data; + graphene_rect_t area; + + graphene_rect_init (&area, + item->cell_area.x - icon_view->priv->item_padding, + item->cell_area.y - icon_view->priv->item_padding, + item->cell_area.width + icon_view->priv->item_padding * 2, + item->cell_area.height + icon_view->priv->item_padding * 2); + + if (gdk_rectangle_intersect (&item->cell_area, + &(GdkRectangle) { offset_x, offset_y, width, height }, NULL)) + { + gtk_icon_view_snapshot_item (icon_view, snapshot, item, + item->cell_area.x, item->cell_area.y, + icon_view->priv->draw_focus); + + if (dest_index == item->index) + dest_item = item; + } + } + + if (dest_item && + dest_pos != GTK_ICON_VIEW_NO_DROP) + { + GdkRectangle rect = { 0 }; + + switch (dest_pos) + { + case GTK_ICON_VIEW_DROP_INTO: + rect = dest_item->cell_area; + break; + case GTK_ICON_VIEW_DROP_ABOVE: + rect.x = dest_item->cell_area.x; + rect.y = dest_item->cell_area.y - 1; + rect.width = dest_item->cell_area.width; + rect.height = 2; + break; + case GTK_ICON_VIEW_DROP_LEFT: + rect.x = dest_item->cell_area.x - 1; + rect.y = dest_item->cell_area.y; + rect.width = 2; + rect.height = dest_item->cell_area.height; + break; + case GTK_ICON_VIEW_DROP_BELOW: + rect.x = dest_item->cell_area.x; + rect.y = dest_item->cell_area.y + dest_item->cell_area.height - 1; + rect.width = dest_item->cell_area.width; + rect.height = 2; + break; + case GTK_ICON_VIEW_DROP_RIGHT: + rect.x = dest_item->cell_area.x + dest_item->cell_area.width - 1; + rect.y = dest_item->cell_area.y; + rect.width = 2; + rect.height = dest_item->cell_area.height; + break; + case GTK_ICON_VIEW_NO_DROP: + default: + break; + } + + gtk_style_context_save_to_node (context, icon_view->priv->dndnode); + gtk_style_context_set_state (context, gtk_style_context_get_state (context) | GTK_STATE_FLAG_DROP_ACTIVE); + + gtk_snapshot_render_frame (snapshot, context, + rect.x, rect.y, + rect.width, rect.height); + + gtk_style_context_restore (context); + } + + if (icon_view->priv->doing_rubberband) + gtk_icon_view_snapshot_rubberband (icon_view, snapshot); + + gtk_snapshot_restore (snapshot); + + GTK_WIDGET_CLASS (gtk_icon_view_parent_class)->snapshot (widget, snapshot); +} + +static gboolean +rubberband_scroll_timeout (gpointer data) +{ + GtkIconView *icon_view = data; + + gtk_adjustment_set_value (icon_view->priv->vadjustment, + gtk_adjustment_get_value (icon_view->priv->vadjustment) + + icon_view->priv->scroll_value_diff); + + gtk_icon_view_update_rubberband (icon_view); + + return TRUE; +} + +static GtkIconViewItem * +_gtk_icon_view_get_item_at_widget_coords (GtkIconView *icon_view, + int x, + int y, + gboolean only_in_cell, + GtkCellRenderer **cell_at_pos) +{ + x += gtk_adjustment_get_value (icon_view->priv->hadjustment); + y += gtk_adjustment_get_value (icon_view->priv->vadjustment); + + return _gtk_icon_view_get_item_at_coords (icon_view, x, y, + only_in_cell, cell_at_pos); +} + +static void +gtk_icon_view_motion (GtkEventController *controller, + double x, + double y, + gpointer user_data) +{ + GtkIconView *icon_view; + int abs_y; + GdkDevice *device; + + icon_view = GTK_ICON_VIEW (user_data); + + icon_view->priv->mouse_x = x; + icon_view->priv->mouse_y = y; + + device = gtk_event_controller_get_current_event_device (controller); + gtk_icon_view_maybe_begin_drag (icon_view, x, y, device); + + if (icon_view->priv->doing_rubberband) + { + int height; + gtk_icon_view_update_rubberband (icon_view); + + abs_y = icon_view->priv->mouse_y - icon_view->priv->height * + (gtk_adjustment_get_value (icon_view->priv->vadjustment) / + (gtk_adjustment_get_upper (icon_view->priv->vadjustment) - + gtk_adjustment_get_lower (icon_view->priv->vadjustment))); + + height = gtk_widget_get_height (GTK_WIDGET (icon_view)); + + + if (abs_y < 0 || abs_y > height) + { + if (abs_y < 0) + icon_view->priv->scroll_value_diff = abs_y; + else + icon_view->priv->scroll_value_diff = abs_y - height; + + icon_view->priv->event_last_x = icon_view->priv->mouse_x; + icon_view->priv->event_last_y = icon_view->priv->mouse_x; + + if (icon_view->priv->scroll_timeout_id == 0) { + icon_view->priv->scroll_timeout_id = g_timeout_add (30, rubberband_scroll_timeout, icon_view); + gdk_source_set_static_name_by_id (icon_view->priv->scroll_timeout_id, "[gtk] rubberband_scroll_timeout"); + } + } + else + remove_scroll_timeout (icon_view); + } + else + { + GtkIconViewItem *item, *last_prelight_item; + GtkCellRenderer *cell = NULL; + + last_prelight_item = icon_view->priv->last_prelight; + item = _gtk_icon_view_get_item_at_widget_coords (icon_view, + icon_view->priv->mouse_x, + icon_view->priv->mouse_y, + FALSE, + &cell); + + if (item != last_prelight_item) + { + if (item != NULL) + { + gtk_icon_view_queue_draw_item (icon_view, item); + } + + if (last_prelight_item != NULL) + { + gtk_icon_view_queue_draw_item (icon_view, + icon_view->priv->last_prelight); + } + + icon_view->priv->last_prelight = item; + } + } +} + +static void +gtk_icon_view_leave (GtkEventController *controller, + gpointer user_data) +{ + GtkIconView *icon_view; + GtkIconViewPrivate *priv; + + icon_view = GTK_ICON_VIEW (user_data); + priv = icon_view->priv; + + if (priv->last_prelight) + { + gtk_icon_view_queue_draw_item (icon_view, priv->last_prelight); + priv->last_prelight = NULL; + } +} + +static void +gtk_icon_view_remove (GtkIconView *icon_view, + GtkWidget *widget) +{ + GtkIconViewChild *child = NULL; + GList *tmp_list; + + tmp_list = icon_view->priv->children; + while (tmp_list) + { + child = tmp_list->data; + if (child->widget == widget) + { + gtk_widget_unparent (widget); + + icon_view->priv->children = g_list_remove_link (icon_view->priv->children, tmp_list); + g_list_free_1 (tmp_list); + g_free (child); + return; + } + + tmp_list = tmp_list->next; + } +} + +static void +gtk_icon_view_add_editable (GtkCellArea *area, + GtkCellRenderer *renderer, + GtkCellEditable *editable, + GdkRectangle *cell_area, + const char *path, + GtkIconView *icon_view) +{ + GtkIconViewChild *child; + GtkWidget *widget = GTK_WIDGET (editable); + + child = g_new (GtkIconViewChild, 1); + + child->widget = widget; + child->area.x = cell_area->x; + child->area.y = cell_area->y; + child->area.width = cell_area->width; + child->area.height = cell_area->height; + + icon_view->priv->children = g_list_append (icon_view->priv->children, child); + + gtk_widget_set_parent (widget, GTK_WIDGET (icon_view)); +} + +static void +gtk_icon_view_remove_editable (GtkCellArea *area, + GtkCellRenderer *renderer, + GtkCellEditable *editable, + GtkIconView *icon_view) +{ + GtkTreePath *path; + + if (gtk_widget_has_focus (GTK_WIDGET (editable))) + gtk_widget_grab_focus (GTK_WIDGET (icon_view)); + + gtk_icon_view_remove (icon_view, GTK_WIDGET (editable)); + + path = gtk_tree_path_new_from_string (gtk_cell_area_get_current_path_string (area)); + gtk_icon_view_queue_draw_path (icon_view, path); + gtk_tree_path_free (path); +} + +/** + * gtk_icon_view_set_cursor: + * @icon_view: A `GtkIconView` + * @path: A `GtkTreePath` + * @cell: (nullable): One of the cell renderers of @icon_view + * @start_editing: %TRUE if the specified cell should start being edited. + * + * Sets the current keyboard focus to be at @path, and selects it. This is + * useful when you want to focus the user’s attention on a particular item. + * If @cell is not %NULL, then focus is given to the cell specified by + * it. Additionally, if @start_editing is %TRUE, then editing should be + * started in the specified cell. + * + * This function is often followed by `gtk_widget_grab_focus + * (icon_view)` in order to give keyboard focus to the widget. + * Please note that editing can only happen when the widget is realized. + * + * Deprecated: 4.10: Use GtkGridView instead + **/ +void +gtk_icon_view_set_cursor (GtkIconView *icon_view, + GtkTreePath *path, + GtkCellRenderer *cell, + gboolean start_editing) +{ + GtkIconViewItem *item = NULL; + + g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); + g_return_if_fail (path != NULL); + g_return_if_fail (cell == NULL || GTK_IS_CELL_RENDERER (cell)); + + if (icon_view->priv->cell_area) + gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); + + if (gtk_tree_path_get_depth (path) == 1) + item = g_list_nth_data (icon_view->priv->items, + gtk_tree_path_get_indices(path)[0]); + + if (!item) + return; + + _gtk_icon_view_set_cursor_item (icon_view, item, cell); + gtk_icon_view_scroll_to_path (icon_view, path, FALSE, 0.0, 0.0); + + if (start_editing && + icon_view->priv->cell_area) + { + GtkCellAreaContext *context; + + context = g_ptr_array_index (icon_view->priv->row_contexts, item->row); + _gtk_icon_view_set_cell_data (icon_view, item); + gtk_cell_area_activate (icon_view->priv->cell_area, context, + GTK_WIDGET (icon_view), &item->cell_area, + 0, TRUE); + } +} + +/** + * gtk_icon_view_get_cursor: + * @icon_view: A `GtkIconView` + * @path: (out) (optional) (transfer full): Return location for the current + * cursor path + * @cell: (out) (optional) (transfer none): Return location the current + * focus cell + * + * Fills in @path and @cell with the current cursor path and cell. + * If the cursor isn’t currently set, then *@path will be %NULL. + * If no cell currently has focus, then *@cell will be %NULL. + * + * The returned `GtkTreePath` must be freed with gtk_tree_path_free(). + * + * Returns: %TRUE if the cursor is set. + * + * Deprecated: 4.10: Use GtkGridView instead + **/ +gboolean +gtk_icon_view_get_cursor (GtkIconView *icon_view, + GtkTreePath **path, + GtkCellRenderer **cell) +{ + GtkIconViewItem *item; + + g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), FALSE); + + item = icon_view->priv->cursor_item; + + if (path != NULL) + { + if (item != NULL) + *path = gtk_tree_path_new_from_indices (item->index, -1); + else + *path = NULL; + } + + if (cell != NULL && item != NULL && icon_view->priv->cell_area != NULL) + *cell = gtk_cell_area_get_focus_cell (icon_view->priv->cell_area); + + return (item != NULL); +} + +static void +gtk_icon_view_button_press (GtkGestureClick *gesture, + int n_press, + double x, + double y, + gpointer user_data) +{ + GtkIconView *icon_view = user_data; + GtkWidget *widget = GTK_WIDGET (icon_view); + GtkIconViewItem *item; + gboolean dirty = FALSE; + GtkCellRenderer *cell = NULL, *cursor_cell = NULL; + int button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture)); + GdkEventSequence *sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture)); + GdkEvent *event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence); + GdkModifierType state; + + if (!gtk_widget_has_focus (widget)) + gtk_widget_grab_focus (widget); + + if (button == GDK_BUTTON_PRIMARY) + { + GdkModifierType extend_mod_mask = GDK_SHIFT_MASK; + GdkModifierType modify_mod_mask = GDK_CONTROL_MASK; + + state = gdk_event_get_modifier_state (event); + + item = _gtk_icon_view_get_item_at_widget_coords (icon_view, + x, y, + FALSE, + &cell); + + /* + * We consider only the cells' area as the item area if the + * item is not selected, but if it *is* selected, the complete + * selection rectangle is considered to be part of the item. + */ + if (item != NULL && (cell != NULL || item->selected)) + { + if (cell != NULL) + { + if (gtk_cell_renderer_is_activatable (cell)) + cursor_cell = cell; + } + + gtk_icon_view_scroll_to_item (icon_view, item); + + if (icon_view->priv->selection_mode == GTK_SELECTION_NONE) + { + _gtk_icon_view_set_cursor_item (icon_view, item, cursor_cell); + } + else if (icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE && + (state & extend_mod_mask)) + { + gtk_icon_view_unselect_all_internal (icon_view); + + _gtk_icon_view_set_cursor_item (icon_view, item, cursor_cell); + if (!icon_view->priv->anchor_item) + icon_view->priv->anchor_item = item; + else + gtk_icon_view_select_all_between (icon_view, + icon_view->priv->anchor_item, + item); + dirty = TRUE; + } + else + { + if ((icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE || + ((icon_view->priv->selection_mode == GTK_SELECTION_SINGLE) && item->selected)) && + (state & modify_mod_mask)) + { + item->selected = !item->selected; + gtk_icon_view_queue_draw_item (icon_view, item); + dirty = TRUE; + } + else + { + gtk_icon_view_unselect_all_internal (icon_view); + + item->selected = TRUE; + gtk_icon_view_queue_draw_item (icon_view, item); + dirty = TRUE; + } + _gtk_icon_view_set_cursor_item (icon_view, item, cursor_cell); + icon_view->priv->anchor_item = item; + } + + /* Save press to possibly begin a drag */ + if (icon_view->priv->pressed_button < 0) + { + icon_view->priv->pressed_button = button; + icon_view->priv->press_start_x = x; + icon_view->priv->press_start_y = y; + } + + icon_view->priv->last_single_clicked = item; + + /* cancel the current editing, if it exists */ + gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); + + if (cell != NULL && gtk_cell_renderer_is_activatable (cell)) + { + GtkCellAreaContext *context; + + context = g_ptr_array_index (icon_view->priv->row_contexts, item->row); + + _gtk_icon_view_set_cell_data (icon_view, item); + gtk_cell_area_activate (icon_view->priv->cell_area, context, + GTK_WIDGET (icon_view), + &item->cell_area, 0, FALSE); + } + } + else + { + if (icon_view->priv->selection_mode != GTK_SELECTION_BROWSE && + !(state & modify_mod_mask)) + { + dirty = gtk_icon_view_unselect_all_internal (icon_view); + } + + if (icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE) + gtk_icon_view_start_rubberbanding (icon_view, + gtk_gesture_get_device (GTK_GESTURE (gesture)), + x, y); + } + + /* don't draw keyboard focus around a clicked-on item */ + icon_view->priv->draw_focus = FALSE; + } + + if (!icon_view->priv->activate_on_single_click + && button == GDK_BUTTON_PRIMARY + && n_press == 2) + { + item = _gtk_icon_view_get_item_at_widget_coords (icon_view, + x, y, + FALSE, + NULL); + + if (item && item == icon_view->priv->last_single_clicked) + { + GtkTreePath *path; + + path = gtk_tree_path_new_from_indices (item->index, -1); + gtk_icon_view_item_activated (icon_view, path); + gtk_tree_path_free (path); + } + + icon_view->priv->last_single_clicked = NULL; + icon_view->priv->pressed_button = -1; + } + + if (dirty) + g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0); +} + +static gboolean +button_event_modifies_selection (GdkEvent *event) +{ + guint state = gdk_event_get_modifier_state (event); + + return (state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) != 0; +} + +static void +gtk_icon_view_button_release (GtkGestureClick *gesture, + int n_press, + double x, + double y, + gpointer user_data) +{ + GtkIconView *icon_view = user_data; + int button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture)); + GdkEventSequence *sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture)); + GdkEvent *event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence); + + if (icon_view->priv->pressed_button == button) + icon_view->priv->pressed_button = -1; + + gtk_icon_view_stop_rubberbanding (icon_view); + + remove_scroll_timeout (icon_view); + + if (button == GDK_BUTTON_PRIMARY + && icon_view->priv->activate_on_single_click + && !button_event_modifies_selection (event) + && icon_view->priv->last_single_clicked != NULL) + { + GtkIconViewItem *item; + + item = _gtk_icon_view_get_item_at_widget_coords (icon_view, + x, y, + FALSE, + NULL); + if (item == icon_view->priv->last_single_clicked) + { + GtkTreePath *path; + path = gtk_tree_path_new_from_indices (item->index, -1); + gtk_icon_view_item_activated (icon_view, path); + gtk_tree_path_free (path); + } + + icon_view->priv->last_single_clicked = NULL; + } +} + +static gboolean +gtk_icon_view_key_pressed (GtkEventControllerKey *controller, + guint keyval, + guint keycode, + GdkModifierType state, + GtkWidget *widget) +{ + GtkIconView *icon_view = GTK_ICON_VIEW (widget); + + if (icon_view->priv->doing_rubberband) + { + if (keyval == GDK_KEY_Escape) + gtk_icon_view_stop_rubberbanding (icon_view); + + return TRUE; + } + + return FALSE; +} + +static void +gtk_icon_view_update_rubberband (GtkIconView *icon_view) +{ + int x, y; + + x = MAX (icon_view->priv->mouse_x, 0); + y = MAX (icon_view->priv->mouse_y, 0); + + icon_view->priv->rubberband_x2 = x + gtk_adjustment_get_value (icon_view->priv->hadjustment); + icon_view->priv->rubberband_y2 = y + gtk_adjustment_get_value (icon_view->priv->vadjustment); + + gtk_icon_view_update_rubberband_selection (icon_view); + gtk_widget_queue_draw (GTK_WIDGET (icon_view)); +} + +static void +gtk_icon_view_start_rubberbanding (GtkIconView *icon_view, + GdkDevice *device, + int x, + int y) +{ + GtkIconViewPrivate *priv = icon_view->priv; + GList *items; + GtkCssNode *widget_node; + + if (priv->rubberband_device) + return; + + for (items = priv->items; items; items = items->next) + { + GtkIconViewItem *item = items->data; + + item->selected_before_rubberbanding = item->selected; + } + + priv->rubberband_x1 = x + gtk_adjustment_get_value (priv->hadjustment); + priv->rubberband_y1 = y + gtk_adjustment_get_value (priv->vadjustment); + priv->rubberband_x2 = priv->rubberband_x1; + priv->rubberband_y2 = priv->rubberband_y1; + + priv->doing_rubberband = TRUE; + priv->rubberband_device = device; + + widget_node = gtk_widget_get_css_node (GTK_WIDGET (icon_view)); + priv->rubberband_node = gtk_css_node_new (); + gtk_css_node_set_name (priv->rubberband_node, g_quark_from_static_string ("rubberband")); + gtk_css_node_set_parent (priv->rubberband_node, widget_node); + gtk_css_node_set_state (priv->rubberband_node, gtk_css_node_get_state (widget_node)); + g_object_unref (priv->rubberband_node); +} + +static void +gtk_icon_view_stop_rubberbanding (GtkIconView *icon_view) +{ + GtkIconViewPrivate *priv = icon_view->priv; + + if (!priv->doing_rubberband) + return; + + priv->doing_rubberband = FALSE; + priv->rubberband_device = NULL; + gtk_css_node_set_parent (priv->rubberband_node, NULL); + priv->rubberband_node = NULL; + + gtk_widget_queue_draw (GTK_WIDGET (icon_view)); +} + +static void +gtk_icon_view_update_rubberband_selection (GtkIconView *icon_view) +{ + GList *items; + int x, y, width, height; + gboolean dirty = FALSE; + + x = MIN (icon_view->priv->rubberband_x1, + icon_view->priv->rubberband_x2); + y = MIN (icon_view->priv->rubberband_y1, + icon_view->priv->rubberband_y2); + width = ABS (icon_view->priv->rubberband_x1 - + icon_view->priv->rubberband_x2); + height = ABS (icon_view->priv->rubberband_y1 - + icon_view->priv->rubberband_y2); + + for (items = icon_view->priv->items; items; items = items->next) + { + GtkIconViewItem *item = items->data; + gboolean is_in; + gboolean selected; + + is_in = gtk_icon_view_item_hit_test (icon_view, item, + x, y, width, height); + + selected = is_in ^ item->selected_before_rubberbanding; + + if (item->selected != selected) + { + item->selected = selected; + dirty = TRUE; + gtk_icon_view_queue_draw_item (icon_view, item); + } + } + + if (dirty) + g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0); +} + + +typedef struct { + GdkRectangle hit_rect; + gboolean hit; +} HitTestData; + +static gboolean +hit_test (GtkCellRenderer *renderer, + const GdkRectangle *cell_area, + const GdkRectangle *cell_background, + HitTestData *data) +{ + if (MIN (data->hit_rect.x + data->hit_rect.width, cell_area->x + cell_area->width) - + MAX (data->hit_rect.x, cell_area->x) > 0 && + MIN (data->hit_rect.y + data->hit_rect.height, cell_area->y + cell_area->height) - + MAX (data->hit_rect.y, cell_area->y) > 0) + data->hit = TRUE; + + return (data->hit != FALSE); +} + +static gboolean +gtk_icon_view_item_hit_test (GtkIconView *icon_view, + GtkIconViewItem *item, + int x, + int y, + int width, + int height) +{ + HitTestData data = { { x, y, width, height }, FALSE }; + GtkCellAreaContext *context; + GdkRectangle *item_area = &item->cell_area; + + if (MIN (x + width, item_area->x + item_area->width) - MAX (x, item_area->x) <= 0 || + MIN (y + height, item_area->y + item_area->height) - MAX (y, item_area->y) <= 0) + return FALSE; + + context = g_ptr_array_index (icon_view->priv->row_contexts, item->row); + + _gtk_icon_view_set_cell_data (icon_view, item); + gtk_cell_area_foreach_alloc (icon_view->priv->cell_area, context, + GTK_WIDGET (icon_view), + item_area, item_area, + (GtkCellAllocCallback)hit_test, &data); + + return data.hit; +} + +static gboolean +gtk_icon_view_unselect_all_internal (GtkIconView *icon_view) +{ + gboolean dirty = FALSE; + GList *items; + + if (icon_view->priv->selection_mode == GTK_SELECTION_NONE) + return FALSE; + + for (items = icon_view->priv->items; items; items = items->next) + { + GtkIconViewItem *item = items->data; + + if (item->selected) + { + item->selected = FALSE; + dirty = TRUE; + gtk_icon_view_queue_draw_item (icon_view, item); + } + } + + return dirty; +} + + +/* GtkIconView signals */ +static void +gtk_icon_view_real_select_all (GtkIconView *icon_view) +{ + gtk_icon_view_select_all (icon_view); +} + +static void +gtk_icon_view_real_unselect_all (GtkIconView *icon_view) +{ + gtk_icon_view_unselect_all (icon_view); +} + +static void +gtk_icon_view_real_select_cursor_item (GtkIconView *icon_view) +{ + gtk_icon_view_unselect_all (icon_view); + + if (icon_view->priv->cursor_item != NULL) + _gtk_icon_view_select_item (icon_view, icon_view->priv->cursor_item); +} + +static gboolean +gtk_icon_view_real_activate_cursor_item (GtkIconView *icon_view) +{ + GtkTreePath *path; + GtkCellAreaContext *context; + + if (!icon_view->priv->cursor_item) + return FALSE; + + context = g_ptr_array_index (icon_view->priv->row_contexts, icon_view->priv->cursor_item->row); + + _gtk_icon_view_set_cell_data (icon_view, icon_view->priv->cursor_item); + gtk_cell_area_activate (icon_view->priv->cell_area, context, + GTK_WIDGET (icon_view), + &icon_view->priv->cursor_item->cell_area, + 0, + FALSE); + + path = gtk_tree_path_new_from_indices (icon_view->priv->cursor_item->index, -1); + gtk_icon_view_item_activated (icon_view, path); + gtk_tree_path_free (path); + + return TRUE; +} + +static void +gtk_icon_view_real_toggle_cursor_item (GtkIconView *icon_view) +{ + if (!icon_view->priv->cursor_item) + return; + + switch (icon_view->priv->selection_mode) + { + case GTK_SELECTION_NONE: + default: + break; + case GTK_SELECTION_BROWSE: + _gtk_icon_view_select_item (icon_view, icon_view->priv->cursor_item); + break; + case GTK_SELECTION_SINGLE: + if (icon_view->priv->cursor_item->selected) + _gtk_icon_view_unselect_item (icon_view, icon_view->priv->cursor_item); + else + _gtk_icon_view_select_item (icon_view, icon_view->priv->cursor_item); + break; + case GTK_SELECTION_MULTIPLE: + icon_view->priv->cursor_item->selected = !icon_view->priv->cursor_item->selected; + g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0); + + gtk_icon_view_queue_draw_item (icon_view, icon_view->priv->cursor_item); + break; + } +} + +static void +gtk_icon_view_set_hadjustment_values (GtkIconView *icon_view) +{ + int width; + GtkAdjustment *adj = icon_view->priv->hadjustment; + double old_page_size; + double old_upper; + double old_value; + double new_value; + double new_upper; + + width = gtk_widget_get_width (GTK_WIDGET (icon_view)); + + old_value = gtk_adjustment_get_value (adj); + old_upper = gtk_adjustment_get_upper (adj); + old_page_size = gtk_adjustment_get_page_size (adj); + new_upper = MAX (width, icon_view->priv->width); + + if (gtk_widget_get_direction (GTK_WIDGET (icon_view)) == GTK_TEXT_DIR_RTL) + { + /* Make sure no scrolling occurs for RTL locales also (if possible) */ + /* Quick explanation: + * In LTR locales, leftmost portion of visible rectangle should stay + * fixed, which means left edge of scrollbar thumb should remain fixed + * and thus adjustment's value should stay the same. + * + * In RTL locales, we want to keep rightmost portion of visible + * rectangle fixed. This means right edge of thumb should remain fixed. + * In this case, upper - value - page_size should remain constant. + */ + new_value = (new_upper - width) - + (old_upper - old_value - old_page_size); + new_value = CLAMP (new_value, 0, new_upper - width); + } + else + new_value = CLAMP (old_value, 0, new_upper - width); + + gtk_adjustment_configure (adj, + new_value, + 0.0, + new_upper, + width * 0.1, + width * 0.9, + width); +} + +static void +gtk_icon_view_set_vadjustment_values (GtkIconView *icon_view) +{ + int height; + GtkAdjustment *adj = icon_view->priv->vadjustment; + + height = gtk_widget_get_height (GTK_WIDGET (icon_view)); + + gtk_adjustment_configure (adj, + gtk_adjustment_get_value (adj), + 0.0, + MAX (height, icon_view->priv->height), + height * 0.1, + height * 0.9, + height); +} + +static void +gtk_icon_view_set_hadjustment (GtkIconView *icon_view, + GtkAdjustment *adjustment) +{ + GtkIconViewPrivate *priv = icon_view->priv; + + if (adjustment && priv->hadjustment == adjustment) + return; + + if (priv->hadjustment != NULL) + { + g_signal_handlers_disconnect_matched (priv->hadjustment, + G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, icon_view); + g_object_unref (priv->hadjustment); + } + + if (!adjustment) + adjustment = gtk_adjustment_new (0.0, 0.0, 0.0, + 0.0, 0.0, 0.0); + + g_signal_connect (adjustment, "value-changed", + G_CALLBACK (gtk_icon_view_adjustment_changed), icon_view); + priv->hadjustment = g_object_ref_sink (adjustment); + gtk_icon_view_set_hadjustment_values (icon_view); + + g_object_notify (G_OBJECT (icon_view), "hadjustment"); +} + +static void +gtk_icon_view_set_vadjustment (GtkIconView *icon_view, + GtkAdjustment *adjustment) +{ + GtkIconViewPrivate *priv = icon_view->priv; + + if (adjustment && priv->vadjustment == adjustment) + return; + + if (priv->vadjustment != NULL) + { + g_signal_handlers_disconnect_matched (priv->vadjustment, + G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, icon_view); + g_object_unref (priv->vadjustment); + } + + if (!adjustment) + adjustment = gtk_adjustment_new (0.0, 0.0, 0.0, + 0.0, 0.0, 0.0); + + g_signal_connect (adjustment, "value-changed", + G_CALLBACK (gtk_icon_view_adjustment_changed), icon_view); + priv->vadjustment = g_object_ref_sink (adjustment); + gtk_icon_view_set_vadjustment_values (icon_view); + + g_object_notify (G_OBJECT (icon_view), "vadjustment"); +} + +static void +gtk_icon_view_adjustment_changed (GtkAdjustment *adjustment, + GtkIconView *icon_view) +{ + GtkWidget *widget = GTK_WIDGET (icon_view); + + if (gtk_widget_get_realized (widget)) + { + if (icon_view->priv->doing_rubberband) + gtk_icon_view_update_rubberband (icon_view); + } + + gtk_widget_queue_draw (GTK_WIDGET (icon_view)); +} + +static int +compare_sizes (gconstpointer p1, + gconstpointer p2, + gpointer unused) +{ + return GPOINTER_TO_INT (((const GtkRequestedSize *) p1)->data) + - GPOINTER_TO_INT (((const GtkRequestedSize *) p2)->data); +} + +static void +gtk_icon_view_layout (GtkIconView *icon_view) +{ + GtkIconViewPrivate *priv = icon_view->priv; + GtkWidget *widget = GTK_WIDGET (icon_view); + GList *items; + int item_width = 0; /* this doesn't include item_padding */ + int n_columns, n_rows, n_items; + int col, row; + GtkRequestedSize *sizes; + gboolean rtl; + int width, height; + + if (gtk_icon_view_is_empty (icon_view)) + return; + + rtl = gtk_widget_get_direction (GTK_WIDGET (icon_view)) == GTK_TEXT_DIR_RTL; + n_items = gtk_icon_view_get_n_items (icon_view); + + width = gtk_widget_get_width (widget); + height = gtk_widget_get_height (widget); + + gtk_icon_view_compute_n_items_for_size (icon_view, + GTK_ORIENTATION_HORIZONTAL, + width, + NULL, NULL, + &n_columns, &item_width); + n_rows = (n_items + n_columns - 1) / n_columns; + + priv->width = n_columns * (item_width + 2 * priv->item_padding + priv->column_spacing) - priv->column_spacing; + priv->width += 2 * priv->margin; + priv->width = MAX (priv->width, width); + + /* Clear the per row contexts */ + g_ptr_array_set_size (icon_view->priv->row_contexts, 0); + + gtk_cell_area_context_reset (priv->cell_area_context); + /* because layouting is complicated. We designed an API + * that is O(N²) and nonsensical. + * And we're proud of it. */ + for (items = priv->items; items; items = items->next) + { + _gtk_icon_view_set_cell_data (icon_view, items->data); + gtk_cell_area_get_preferred_width (priv->cell_area, + priv->cell_area_context, + widget, + NULL, NULL); + } + + sizes = g_newa (GtkRequestedSize, n_rows); + items = priv->items; + priv->height = priv->margin; + + /* Collect the heights for all rows */ + for (row = 0; row < n_rows; row++) + { + GtkCellAreaContext *context = gtk_cell_area_copy_context (priv->cell_area, priv->cell_area_context); + g_ptr_array_add (priv->row_contexts, context); + + for (col = 0; col < n_columns && items; col++, items = items->next) + { + GtkIconViewItem *item = items->data; + + _gtk_icon_view_set_cell_data (icon_view, item); + gtk_cell_area_get_preferred_height_for_width (priv->cell_area, + context, + widget, + item_width, + NULL, NULL); + } + + sizes[row].data = GINT_TO_POINTER (row); + gtk_cell_area_context_get_preferred_height_for_width (context, + item_width, + &sizes[row].minimum_size, + &sizes[row].natural_size); + priv->height += sizes[row].minimum_size + 2 * priv->item_padding + priv->row_spacing; + } + + priv->height -= priv->row_spacing; + priv->height += priv->margin; + priv->height = MIN (priv->height, height); + + gtk_distribute_natural_allocation (height - priv->height, + n_rows, + sizes); + + /* Actually allocate the rows */ + g_qsort_with_data (sizes, n_rows, sizeof (GtkRequestedSize), compare_sizes, NULL); + + items = priv->items; + priv->height = priv->margin; + + for (row = 0; row < n_rows; row++) + { + GtkCellAreaContext *context = g_ptr_array_index (priv->row_contexts, row); + gtk_cell_area_context_allocate (context, item_width, sizes[row].minimum_size); + + priv->height += priv->item_padding; + + for (col = 0; col < n_columns && items; col++, items = items->next) + { + GtkIconViewItem *item = items->data; + + item->cell_area.x = priv->margin + (col * 2 + 1) * priv->item_padding + col * (priv->column_spacing + item_width); + item->cell_area.width = item_width; + item->cell_area.y = priv->height; + item->cell_area.height = sizes[row].minimum_size; + item->row = row; + item->col = col; + if (rtl) + { + item->cell_area.x = priv->width - item_width - item->cell_area.x; + item->col = n_columns - 1 - col; + } + } + + priv->height += sizes[row].minimum_size + priv->item_padding + priv->row_spacing; + } + + priv->height -= priv->row_spacing; + priv->height += priv->margin; + priv->height = MAX (priv->height, height); +} + +static void +gtk_icon_view_invalidate_sizes (GtkIconView *icon_view) +{ + /* Clear all item sizes */ + g_list_foreach (icon_view->priv->items, + (GFunc)gtk_icon_view_item_invalidate_size, NULL); + + /* Re-layout the items */ + gtk_widget_queue_resize (GTK_WIDGET (icon_view)); +} + +static void +gtk_icon_view_item_invalidate_size (GtkIconViewItem *item) +{ + item->cell_area.width = -1; + item->cell_area.height = -1; +} + +static void +gtk_icon_view_snapshot_item (GtkIconView *icon_view, + GtkSnapshot *snapshot, + GtkIconViewItem *item, + int x, + int y, + gboolean draw_focus) +{ + GdkRectangle cell_area; + GtkStateFlags state = 0; + GtkCellRendererState flags = 0; + GtkStyleContext *style_context; + GtkWidget *widget = GTK_WIDGET (icon_view); + GtkIconViewPrivate *priv = icon_view->priv; + GtkCellAreaContext *context; + + if (priv->model == NULL || item->cell_area.width <= 0 || item->cell_area.height <= 0) + return; + + _gtk_icon_view_set_cell_data (icon_view, item); + + style_context = gtk_widget_get_style_context (widget); + state = gtk_widget_get_state_flags (widget); + + gtk_style_context_save (style_context); + gtk_style_context_add_class (style_context, "cell"); + + state &= ~(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_PRELIGHT); + + if ((state & GTK_STATE_FLAG_FOCUSED) && + item == icon_view->priv->cursor_item) + flags |= GTK_CELL_RENDERER_FOCUSED; + + if (item->selected) + { + state |= GTK_STATE_FLAG_SELECTED; + flags |= GTK_CELL_RENDERER_SELECTED; + } + + if (item == priv->last_prelight) + { + state |= GTK_STATE_FLAG_PRELIGHT; + flags |= GTK_CELL_RENDERER_PRELIT; + } + + gtk_style_context_set_state (style_context, state); + + gtk_snapshot_render_background (snapshot, style_context, + x - priv->item_padding, + y - priv->item_padding, + item->cell_area.width + priv->item_padding * 2, + item->cell_area.height + priv->item_padding * 2); + gtk_snapshot_render_frame (snapshot, style_context, + x - priv->item_padding, + y - priv->item_padding, + item->cell_area.width + priv->item_padding * 2, + item->cell_area.height + priv->item_padding * 2); + + cell_area.x = x; + cell_area.y = y; + cell_area.width = item->cell_area.width; + cell_area.height = item->cell_area.height; + + context = g_ptr_array_index (priv->row_contexts, item->row); + gtk_cell_area_snapshot (priv->cell_area, context, + widget, snapshot, &cell_area, &cell_area, flags, + draw_focus); + + gtk_style_context_restore (style_context); +} + +static void +gtk_icon_view_snapshot_rubberband (GtkIconView *icon_view, + GtkSnapshot *snapshot) +{ + GtkIconViewPrivate *priv = icon_view->priv; + GtkStyleContext *context; + GdkRectangle rect; + + rect.x = MIN (priv->rubberband_x1, priv->rubberband_x2); + rect.y = MIN (priv->rubberband_y1, priv->rubberband_y2); + rect.width = ABS (priv->rubberband_x1 - priv->rubberband_x2) + 1; + rect.height = ABS (priv->rubberband_y1 - priv->rubberband_y2) + 1; + + context = gtk_widget_get_style_context (GTK_WIDGET (icon_view)); + + gtk_style_context_save_to_node (context, priv->rubberband_node); + + gtk_snapshot_render_background (snapshot, context, + rect.x, rect.y, + rect.width, rect.height); + gtk_snapshot_render_frame (snapshot, context, + rect.x, rect.y, + rect.width, rect.height); + + gtk_style_context_restore (context); +} + +static void +gtk_icon_view_queue_draw_path (GtkIconView *icon_view, + GtkTreePath *path) +{ + GList *l; + int index; + + index = gtk_tree_path_get_indices (path)[0]; + + for (l = icon_view->priv->items; l; l = l->next) + { + GtkIconViewItem *item = l->data; + + if (item->index == index) + { + gtk_icon_view_queue_draw_item (icon_view, item); + break; + } + } +} + +static void +gtk_icon_view_queue_draw_item (GtkIconView *icon_view, + GtkIconViewItem *item) +{ + gtk_widget_queue_draw (GTK_WIDGET (icon_view)); +} + +void +_gtk_icon_view_set_cursor_item (GtkIconView *icon_view, + GtkIconViewItem *item, + GtkCellRenderer *cursor_cell) +{ + /* When hitting this path from keynav, the focus cell is already set, + * but we still need to queue the draw here (in the case that the focus + * cell changes but not the cursor item). + */ + gtk_icon_view_queue_draw_item (icon_view, item); + + if (icon_view->priv->cursor_item == item && + (cursor_cell == NULL || cursor_cell == gtk_cell_area_get_focus_cell (icon_view->priv->cell_area))) + return; + + if (icon_view->priv->cursor_item != NULL) + gtk_icon_view_queue_draw_item (icon_view, icon_view->priv->cursor_item); + + icon_view->priv->cursor_item = item; + + if (cursor_cell) + gtk_cell_area_set_focus_cell (icon_view->priv->cell_area, cursor_cell); + else + { + /* Make sure there is a cell in focus initially */ + if (!gtk_cell_area_get_focus_cell (icon_view->priv->cell_area)) + gtk_cell_area_focus (icon_view->priv->cell_area, GTK_DIR_TAB_FORWARD); + } +} + + +static GtkIconViewItem * +gtk_icon_view_item_new (void) +{ + GtkIconViewItem *item; + + item = g_slice_new0 (GtkIconViewItem); + + item->cell_area.width = -1; + item->cell_area.height = -1; + + return item; +} + +static void +gtk_icon_view_item_free (GtkIconViewItem *item) +{ + g_return_if_fail (item != NULL); + + g_slice_free (GtkIconViewItem, item); +} + +GtkIconViewItem * +_gtk_icon_view_get_item_at_coords (GtkIconView *icon_view, + int x, + int y, + gboolean only_in_cell, + GtkCellRenderer **cell_at_pos) +{ + GList *items; + + if (cell_at_pos) + *cell_at_pos = NULL; + + for (items = icon_view->priv->items; items; items = items->next) + { + GtkIconViewItem *item = items->data; + GdkRectangle *item_area = &item->cell_area; + + if (x >= item_area->x - icon_view->priv->column_spacing/2 && + x <= item_area->x + item_area->width + icon_view->priv->column_spacing/2 && + y >= item_area->y - icon_view->priv->row_spacing/2 && + y <= item_area->y + item_area->height + icon_view->priv->row_spacing/2) + { + if (only_in_cell || cell_at_pos) + { + GtkCellRenderer *cell = NULL; + GtkCellAreaContext *context; + + context = g_ptr_array_index (icon_view->priv->row_contexts, item->row); + _gtk_icon_view_set_cell_data (icon_view, item); + + if (x >= item_area->x && x <= item_area->x + item_area->width && + y >= item_area->y && y <= item_area->y + item_area->height) + cell = gtk_cell_area_get_cell_at_position (icon_view->priv->cell_area, context, + GTK_WIDGET (icon_view), + item_area, + x, y, NULL); + + if (cell_at_pos) + *cell_at_pos = cell; + + if (only_in_cell) + return cell != NULL ? item : NULL; + else + return item; + } + return item; + } + } + return NULL; +} + +void +_gtk_icon_view_select_item (GtkIconView *icon_view, + GtkIconViewItem *item) +{ + g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); + g_return_if_fail (item != NULL); + + if (item->selected) + return; + + if (icon_view->priv->selection_mode == GTK_SELECTION_NONE) + return; + else if (icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE) + gtk_icon_view_unselect_all_internal (icon_view); + + item->selected = TRUE; + + g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0); + + gtk_icon_view_queue_draw_item (icon_view, item); +} + + +void +_gtk_icon_view_unselect_item (GtkIconView *icon_view, + GtkIconViewItem *item) +{ + g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); + g_return_if_fail (item != NULL); + + if (!item->selected) + return; + + if (icon_view->priv->selection_mode == GTK_SELECTION_NONE || + icon_view->priv->selection_mode == GTK_SELECTION_BROWSE) + return; + + item->selected = FALSE; + + g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0); + + gtk_icon_view_queue_draw_item (icon_view, item); +} + +static void +verify_items (GtkIconView *icon_view) +{ + GList *items; + int i = 0; + + for (items = icon_view->priv->items; items; items = items->next) + { + GtkIconViewItem *item = items->data; + + if (item->index != i) + g_error ("List item does not match its index: " + "item index %d and list index %d\n", item->index, i); + + i++; + } +} + +static void +gtk_icon_view_row_changed (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + GtkIconView *icon_view = GTK_ICON_VIEW (data); + + /* ignore changes in branches */ + if (gtk_tree_path_get_depth (path) > 1) + return; + + /* An icon view subclass might add it's own model and populate + * things at init() time instead of waiting for the constructor() + * to be called + */ + if (icon_view->priv->cell_area) + gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); + + /* Here we can use a "grow-only" strategy for optimization + * and only invalidate a single item and queue a relayout + * instead of invalidating the whole thing. + * + * For now GtkIconView still can't deal with huge models + * so just invalidate the whole thing when the model + * changes. + */ + gtk_icon_view_invalidate_sizes (icon_view); + + verify_items (icon_view); +} + +static void +gtk_icon_view_row_inserted (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + GtkIconView *icon_view = GTK_ICON_VIEW (data); + int index; + GtkIconViewItem *item; + GList *list; + + /* ignore changes in branches */ + if (gtk_tree_path_get_depth (path) > 1) + return; + + gtk_tree_model_ref_node (model, iter); + + index = gtk_tree_path_get_indices(path)[0]; + + item = gtk_icon_view_item_new (); + + item->index = index; + + /* FIXME: We can be more efficient here, + we can store a tail pointer and use that when + appending (which is a rather common operation) + */ + icon_view->priv->items = g_list_insert (icon_view->priv->items, + item, index); + + list = g_list_nth (icon_view->priv->items, index + 1); + for (; list; list = list->next) + { + item = list->data; + + item->index++; + } + + verify_items (icon_view); + + gtk_widget_queue_resize (GTK_WIDGET (icon_view)); +} + +static void +gtk_icon_view_row_deleted (GtkTreeModel *model, + GtkTreePath *path, + gpointer data) +{ + GtkIconView *icon_view = GTK_ICON_VIEW (data); + int index; + GtkIconViewItem *item; + GList *list, *next; + gboolean emit = FALSE; + GtkTreeIter iter; + + /* ignore changes in branches */ + if (gtk_tree_path_get_depth (path) > 1) + return; + + if (gtk_tree_model_get_iter (model, &iter, path)) + gtk_tree_model_unref_node (model, &iter); + + index = gtk_tree_path_get_indices(path)[0]; + + list = g_list_nth (icon_view->priv->items, index); + item = list->data; + + if (icon_view->priv->cell_area) + gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); + + if (item == icon_view->priv->anchor_item) + icon_view->priv->anchor_item = NULL; + + if (item == icon_view->priv->cursor_item) + icon_view->priv->cursor_item = NULL; + + if (item == icon_view->priv->last_prelight) + icon_view->priv->last_prelight = NULL; + + if (item->selected) + emit = TRUE; + + gtk_icon_view_item_free (item); + + for (next = list->next; next; next = next->next) + { + item = next->data; + + item->index--; + } + + icon_view->priv->items = g_list_delete_link (icon_view->priv->items, list); + + verify_items (icon_view); + + gtk_widget_queue_resize (GTK_WIDGET (icon_view)); + + if (emit) + g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0); +} + +static void +gtk_icon_view_rows_reordered (GtkTreeModel *model, + GtkTreePath *parent, + GtkTreeIter *iter, + int *new_order, + gpointer data) +{ + GtkIconView *icon_view = GTK_ICON_VIEW (data); + int i; + int length; + GList *items = NULL, *list; + GtkIconViewItem **item_array; + int *order; + + /* ignore changes in branches */ + if (iter != NULL) + return; + + if (icon_view->priv->cell_area) + gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); + + length = gtk_tree_model_iter_n_children (model, NULL); + + order = g_new (int, length); + for (i = 0; i < length; i++) + order [new_order[i]] = i; + + item_array = g_new (GtkIconViewItem *, length); + for (i = 0, list = icon_view->priv->items; list != NULL; list = list->next, i++) + item_array[order[i]] = list->data; + g_free (order); + + for (i = length - 1; i >= 0; i--) + { + item_array[i]->index = i; + items = g_list_prepend (items, item_array[i]); + } + + g_free (item_array); + g_list_free (icon_view->priv->items); + icon_view->priv->items = items; + + gtk_widget_queue_resize (GTK_WIDGET (icon_view)); + + verify_items (icon_view); +} + +static void +gtk_icon_view_build_items (GtkIconView *icon_view) +{ + GtkTreeIter iter; + int i; + GList *items = NULL; + + if (!gtk_tree_model_get_iter_first (icon_view->priv->model, + &iter)) + return; + + i = 0; + + do + { + GtkIconViewItem *item = gtk_icon_view_item_new (); + + item->index = i; + + i++; + + items = g_list_prepend (items, item); + + } while (gtk_tree_model_iter_next (icon_view->priv->model, &iter)); + + icon_view->priv->items = g_list_reverse (items); +} + +static void +gtk_icon_view_add_move_binding (GtkWidgetClass *widget_class, + guint keyval, + guint modmask, + GtkMovementStep step, + int count) +{ + + gtk_widget_class_add_binding_signal (widget_class, + keyval, modmask, + I_("move-cursor"), + "(iibb)", step, count, FALSE, FALSE); + + gtk_widget_class_add_binding_signal (widget_class, + keyval, GDK_SHIFT_MASK, + "move-cursor", + "(iibb)", step, count, TRUE, FALSE); + + if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK) + return; + + gtk_widget_class_add_binding_signal (widget_class, + keyval, GDK_CONTROL_MASK | GDK_SHIFT_MASK, + "move-cursor", + "(iibb)", step, count, TRUE, TRUE); + + gtk_widget_class_add_binding_signal (widget_class, + keyval, GDK_CONTROL_MASK, + "move-cursor", + "(iibb)", step, count, FALSE, TRUE); +} + +static gboolean +gtk_icon_view_real_move_cursor (GtkIconView *icon_view, + GtkMovementStep step, + int count, + gboolean extend, + gboolean modify) +{ + g_return_val_if_fail (GTK_ICON_VIEW (icon_view), FALSE); + g_return_val_if_fail (step == GTK_MOVEMENT_LOGICAL_POSITIONS || + step == GTK_MOVEMENT_VISUAL_POSITIONS || + step == GTK_MOVEMENT_DISPLAY_LINES || + step == GTK_MOVEMENT_PAGES || + step == GTK_MOVEMENT_BUFFER_ENDS, FALSE); + + if (!gtk_widget_has_focus (GTK_WIDGET (icon_view))) + return FALSE; + + gtk_cell_area_stop_editing (icon_view->priv->cell_area, FALSE); + gtk_widget_grab_focus (GTK_WIDGET (icon_view)); + + icon_view->priv->extend_selection_pressed = extend; + icon_view->priv->modify_selection_pressed = modify; + + switch (step) + { + case GTK_MOVEMENT_LOGICAL_POSITIONS: + case GTK_MOVEMENT_VISUAL_POSITIONS: + gtk_icon_view_move_cursor_left_right (icon_view, count); + break; + case GTK_MOVEMENT_DISPLAY_LINES: + gtk_icon_view_move_cursor_up_down (icon_view, count); + break; + case GTK_MOVEMENT_PAGES: + gtk_icon_view_move_cursor_page_up_down (icon_view, count); + break; + case GTK_MOVEMENT_BUFFER_ENDS: + gtk_icon_view_move_cursor_start_end (icon_view, count); + break; + case GTK_MOVEMENT_WORDS: + case GTK_MOVEMENT_DISPLAY_LINE_ENDS: + case GTK_MOVEMENT_PARAGRAPHS: + case GTK_MOVEMENT_PARAGRAPH_ENDS: + case GTK_MOVEMENT_HORIZONTAL_PAGES: + default: + g_assert_not_reached (); + break; + } + + icon_view->priv->modify_selection_pressed = FALSE; + icon_view->priv->extend_selection_pressed = FALSE; + + icon_view->priv->draw_focus = TRUE; + + return TRUE; +} + +static GtkIconViewItem * +find_item (GtkIconView *icon_view, + GtkIconViewItem *current, + int row_ofs, + int col_ofs) +{ + int row, col; + GList *items; + GtkIconViewItem *item; + + /* FIXME: this could be more efficient + */ + row = current->row + row_ofs; + col = current->col + col_ofs; + + for (items = icon_view->priv->items; items; items = items->next) + { + item = items->data; + if (item->row == row && item->col == col) + return item; + } + + return NULL; +} + +static GtkIconViewItem * +find_item_page_up_down (GtkIconView *icon_view, + GtkIconViewItem *current, + int count) +{ + GList *item, *next; + int y, col; + + col = current->col; + y = current->cell_area.y + count * gtk_adjustment_get_page_size (icon_view->priv->vadjustment); + + item = g_list_find (icon_view->priv->items, current); + if (count > 0) + { + while (item) + { + for (next = item->next; next; next = next->next) + { + if (((GtkIconViewItem *)next->data)->col == col) + break; + } + if (!next || ((GtkIconViewItem *)next->data)->cell_area.y > y) + break; + + item = next; + } + } + else + { + while (item) + { + for (next = item->prev; next; next = next->prev) + { + if (((GtkIconViewItem *)next->data)->col == col) + break; + } + if (!next || ((GtkIconViewItem *)next->data)->cell_area.y < y) + break; + + item = next; + } + } + + if (item) + return item->data; + + return NULL; +} + +static gboolean +gtk_icon_view_select_all_between (GtkIconView *icon_view, + GtkIconViewItem *anchor, + GtkIconViewItem *cursor) +{ + GList *items; + GtkIconViewItem *item; + int row1, row2, col1, col2; + gboolean dirty = FALSE; + + if (anchor->row < cursor->row) + { + row1 = anchor->row; + row2 = cursor->row; + } + else + { + row1 = cursor->row; + row2 = anchor->row; + } + + if (anchor->col < cursor->col) + { + col1 = anchor->col; + col2 = cursor->col; + } + else + { + col1 = cursor->col; + col2 = anchor->col; + } + + for (items = icon_view->priv->items; items; items = items->next) + { + item = items->data; + + if (row1 <= item->row && item->row <= row2 && + col1 <= item->col && item->col <= col2) + { + if (!item->selected) + { + dirty = TRUE; + item->selected = TRUE; + } + gtk_icon_view_queue_draw_item (icon_view, item); + } + } + + return dirty; +} + +static void +gtk_icon_view_move_cursor_up_down (GtkIconView *icon_view, + int count) +{ + GtkIconViewItem *item; + GtkCellRenderer *cell = NULL; + gboolean dirty = FALSE; + int step; + GtkDirectionType direction; + + if (!gtk_widget_has_focus (GTK_WIDGET (icon_view))) + return; + + direction = count < 0 ? GTK_DIR_UP : GTK_DIR_DOWN; + + if (!icon_view->priv->cursor_item) + { + GList *list; + + if (count > 0) + list = icon_view->priv->items; + else + list = g_list_last (icon_view->priv->items); + + if (list) + { + item = list->data; + + /* Give focus to the first cell initially */ + _gtk_icon_view_set_cell_data (icon_view, item); + gtk_cell_area_focus (icon_view->priv->cell_area, direction); + } + else + { + item = NULL; + } + } + else + { + item = icon_view->priv->cursor_item; + step = count > 0 ? 1 : -1; + + /* Save the current focus cell in case we hit the edge */ + cell = gtk_cell_area_get_focus_cell (icon_view->priv->cell_area); + + while (item) + { + _gtk_icon_view_set_cell_data (icon_view, item); + + if (gtk_cell_area_focus (icon_view->priv->cell_area, direction)) + break; + + item = find_item (icon_view, item, step, 0); + } + } + + if (!item) + { + if (!gtk_widget_keynav_failed (GTK_WIDGET (icon_view), direction)) + { + GtkWidget *toplevel = GTK_WIDGET (gtk_widget_get_root (GTK_WIDGET (icon_view))); + if (toplevel) + gtk_widget_child_focus (toplevel, + direction == GTK_DIR_UP ? + GTK_DIR_TAB_BACKWARD : + GTK_DIR_TAB_FORWARD); + + } + + gtk_cell_area_set_focus_cell (icon_view->priv->cell_area, cell); + return; + } + + if (icon_view->priv->modify_selection_pressed || + !icon_view->priv->extend_selection_pressed || + !icon_view->priv->anchor_item || + icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE) + icon_view->priv->anchor_item = item; + + cell = gtk_cell_area_get_focus_cell (icon_view->priv->cell_area); + _gtk_icon_view_set_cursor_item (icon_view, item, cell); + + if (!icon_view->priv->modify_selection_pressed && + icon_view->priv->selection_mode != GTK_SELECTION_NONE) + { + dirty = gtk_icon_view_unselect_all_internal (icon_view); + dirty = gtk_icon_view_select_all_between (icon_view, + icon_view->priv->anchor_item, + item) || dirty; + } + + gtk_icon_view_scroll_to_item (icon_view, item); + + if (dirty) + g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0); +} + +static void +gtk_icon_view_move_cursor_page_up_down (GtkIconView *icon_view, + int count) +{ + GtkIconViewItem *item; + gboolean dirty = FALSE; + + if (!gtk_widget_has_focus (GTK_WIDGET (icon_view))) + return; + + if (!icon_view->priv->cursor_item) + { + GList *list; + + if (count > 0) + list = icon_view->priv->items; + else + list = g_list_last (icon_view->priv->items); + + item = list ? list->data : NULL; + } + else + item = find_item_page_up_down (icon_view, + icon_view->priv->cursor_item, + count); + + if (item == icon_view->priv->cursor_item) + gtk_widget_error_bell (GTK_WIDGET (icon_view)); + + if (!item) + return; + + if (icon_view->priv->modify_selection_pressed || + !icon_view->priv->extend_selection_pressed || + !icon_view->priv->anchor_item || + icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE) + icon_view->priv->anchor_item = item; + + _gtk_icon_view_set_cursor_item (icon_view, item, NULL); + + if (!icon_view->priv->modify_selection_pressed && + icon_view->priv->selection_mode != GTK_SELECTION_NONE) + { + dirty = gtk_icon_view_unselect_all_internal (icon_view); + dirty = gtk_icon_view_select_all_between (icon_view, + icon_view->priv->anchor_item, + item) || dirty; + } + + gtk_icon_view_scroll_to_item (icon_view, item); + + if (dirty) + g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0); +} + +static void +gtk_icon_view_move_cursor_left_right (GtkIconView *icon_view, + int count) +{ + GtkIconViewItem *item; + GtkCellRenderer *cell = NULL; + gboolean dirty = FALSE; + int step; + GtkDirectionType direction; + + if (!gtk_widget_has_focus (GTK_WIDGET (icon_view))) + return; + + direction = count < 0 ? GTK_DIR_LEFT : GTK_DIR_RIGHT; + + if (!icon_view->priv->cursor_item) + { + GList *list; + + if (count > 0) + list = icon_view->priv->items; + else + list = g_list_last (icon_view->priv->items); + + if (list) + { + item = list->data; + + /* Give focus to the first cell initially */ + _gtk_icon_view_set_cell_data (icon_view, item); + gtk_cell_area_focus (icon_view->priv->cell_area, direction); + } + else + { + item = NULL; + } + } + else + { + item = icon_view->priv->cursor_item; + step = count > 0 ? 1 : -1; + + /* Save the current focus cell in case we hit the edge */ + cell = gtk_cell_area_get_focus_cell (icon_view->priv->cell_area); + + while (item) + { + _gtk_icon_view_set_cell_data (icon_view, item); + + if (gtk_cell_area_focus (icon_view->priv->cell_area, direction)) + break; + + item = find_item (icon_view, item, 0, step); + } + } + + if (!item) + { + if (!gtk_widget_keynav_failed (GTK_WIDGET (icon_view), direction)) + { + GtkWidget *toplevel = GTK_WIDGET (gtk_widget_get_root (GTK_WIDGET (icon_view))); + if (toplevel) + gtk_widget_child_focus (toplevel, + direction == GTK_DIR_LEFT ? + GTK_DIR_TAB_BACKWARD : + GTK_DIR_TAB_FORWARD); + + } + + gtk_cell_area_set_focus_cell (icon_view->priv->cell_area, cell); + return; + } + + if (icon_view->priv->modify_selection_pressed || + !icon_view->priv->extend_selection_pressed || + !icon_view->priv->anchor_item || + icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE) + icon_view->priv->anchor_item = item; + + cell = gtk_cell_area_get_focus_cell (icon_view->priv->cell_area); + _gtk_icon_view_set_cursor_item (icon_view, item, cell); + + if (!icon_view->priv->modify_selection_pressed && + icon_view->priv->selection_mode != GTK_SELECTION_NONE) + { + dirty = gtk_icon_view_unselect_all_internal (icon_view); + dirty = gtk_icon_view_select_all_between (icon_view, + icon_view->priv->anchor_item, + item) || dirty; + } + + gtk_icon_view_scroll_to_item (icon_view, item); + + if (dirty) + g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0); +} + +static void +gtk_icon_view_move_cursor_start_end (GtkIconView *icon_view, + int count) +{ + GtkIconViewItem *item; + GList *list; + gboolean dirty = FALSE; + + if (!gtk_widget_has_focus (GTK_WIDGET (icon_view))) + return; + + if (count < 0) + list = icon_view->priv->items; + else + list = g_list_last (icon_view->priv->items); + + item = list ? list->data : NULL; + + if (item == icon_view->priv->cursor_item) + gtk_widget_error_bell (GTK_WIDGET (icon_view)); + + if (!item) + return; + + if (icon_view->priv->modify_selection_pressed || + !icon_view->priv->extend_selection_pressed || + !icon_view->priv->anchor_item || + icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE) + icon_view->priv->anchor_item = item; + + _gtk_icon_view_set_cursor_item (icon_view, item, NULL); + + if (!icon_view->priv->modify_selection_pressed && + icon_view->priv->selection_mode != GTK_SELECTION_NONE) + { + dirty = gtk_icon_view_unselect_all_internal (icon_view); + dirty = gtk_icon_view_select_all_between (icon_view, + icon_view->priv->anchor_item, + item) || dirty; + } + + gtk_icon_view_scroll_to_item (icon_view, item); + + if (dirty) + g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0); +} + +/** + * gtk_icon_view_scroll_to_path: + * @icon_view: A `GtkIconView` + * @path: The path of the item to move to. + * @use_align: whether to use alignment arguments, or %FALSE. + * @row_align: The vertical alignment of the item specified by @path. + * @col_align: The horizontal alignment of the item specified by @path. + * + * Moves the alignments of @icon_view to the position specified by @path. + * @row_align determines where the row is placed, and @col_align determines + * where @column is placed. Both are expected to be between 0.0 and 1.0. + * 0.0 means left/top alignment, 1.0 means right/bottom alignment, 0.5 means + * center. + * + * If @use_align is %FALSE, then the alignment arguments are ignored, and the + * tree does the minimum amount of work to scroll the item onto the screen. + * This means that the item will be scrolled to the edge closest to its current + * position. If the item is currently visible on the screen, nothing is done. + * + * This function only works if the model is set, and @path is a valid row on + * the model. If the model changes before the @icon_view is realized, the + * centered path will be modified to reflect this change. + * + * Deprecated: 4.10: Use GtkGridView instead + **/ +void +gtk_icon_view_scroll_to_path (GtkIconView *icon_view, + GtkTreePath *path, + gboolean use_align, + float row_align, + float col_align) +{ + GtkIconViewItem *item = NULL; + GtkWidget *widget; + + g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); + g_return_if_fail (path != NULL); + g_return_if_fail (row_align >= 0.0 && row_align <= 1.0); + g_return_if_fail (col_align >= 0.0 && col_align <= 1.0); + + widget = GTK_WIDGET (icon_view); + + if (gtk_tree_path_get_depth (path) > 0) + item = g_list_nth_data (icon_view->priv->items, + gtk_tree_path_get_indices(path)[0]); + + if (!item || item->cell_area.width < 0 || + !gtk_widget_get_realized (widget)) + { + if (icon_view->priv->scroll_to_path) + gtk_tree_row_reference_free (icon_view->priv->scroll_to_path); + + icon_view->priv->scroll_to_path = NULL; + + if (path) + icon_view->priv->scroll_to_path = gtk_tree_row_reference_new_proxy (G_OBJECT (icon_view), icon_view->priv->model, path); + + icon_view->priv->scroll_to_use_align = use_align; + icon_view->priv->scroll_to_row_align = row_align; + icon_view->priv->scroll_to_col_align = col_align; + + return; + } + + if (use_align) + { + int width, height; + int x, y; + float offset; + GdkRectangle item_area = + { + item->cell_area.x - icon_view->priv->item_padding, + item->cell_area.y - icon_view->priv->item_padding, + item->cell_area.width + icon_view->priv->item_padding * 2, + item->cell_area.height + icon_view->priv->item_padding * 2 + }; + + x =0; + y =0; + + width = gtk_widget_get_width (widget); + height = gtk_widget_get_height (widget); + + offset = y + item_area.y - row_align * (height - item_area.height); + + gtk_adjustment_set_value (icon_view->priv->vadjustment, + gtk_adjustment_get_value (icon_view->priv->vadjustment) + offset); + + offset = x + item_area.x - col_align * (width - item_area.width); + + gtk_adjustment_set_value (icon_view->priv->hadjustment, + gtk_adjustment_get_value (icon_view->priv->hadjustment) + offset); + } + else + gtk_icon_view_scroll_to_item (icon_view, item); +} + + +static void +gtk_icon_view_scroll_to_item (GtkIconView *icon_view, + GtkIconViewItem *item) +{ + GtkIconViewPrivate *priv = icon_view->priv; + GtkWidget *widget = GTK_WIDGET (icon_view); + GtkAdjustment *hadj, *vadj; + int widget_width, widget_height; + int x, y; + GdkRectangle item_area; + + item_area.x = item->cell_area.x - priv->item_padding; + item_area.y = item->cell_area.y - priv->item_padding; + item_area.width = item->cell_area.width + priv->item_padding * 2; + item_area.height = item->cell_area.height + priv->item_padding * 2; + + widget_width = gtk_widget_get_width (widget); + widget_height = gtk_widget_get_height (widget); + + hadj = icon_view->priv->hadjustment; + vadj = icon_view->priv->vadjustment; + + x = - gtk_adjustment_get_value (hadj); + y = - gtk_adjustment_get_value (vadj); + + if (y + item_area.y < 0) + gtk_adjustment_animate_to_value (vadj, + gtk_adjustment_get_value (vadj) + + y + item_area.y); + else if (y + item_area.y + item_area.height > widget_height) + gtk_adjustment_animate_to_value (vadj, + gtk_adjustment_get_value (vadj) + + y + item_area.y + item_area.height - widget_height); + + if (x + item_area.x < 0) + gtk_adjustment_animate_to_value (hadj, + gtk_adjustment_get_value (hadj) + + x + item_area.x); + else if (x + item_area.x + item_area.width > widget_width) + gtk_adjustment_animate_to_value (hadj, + gtk_adjustment_get_value (hadj) + + x + item_area.x + item_area.width - widget_width); +} + +/* GtkCellLayout implementation */ + +static void +gtk_icon_view_ensure_cell_area (GtkIconView *icon_view, + GtkCellArea *cell_area) +{ + GtkIconViewPrivate *priv = icon_view->priv; + + if (priv->cell_area) + return; + + if (cell_area) + priv->cell_area = cell_area; + else + priv->cell_area = gtk_cell_area_box_new (); + + g_object_ref_sink (priv->cell_area); + + if (GTK_IS_ORIENTABLE (priv->cell_area)) + gtk_orientable_set_orientation (GTK_ORIENTABLE (priv->cell_area), priv->item_orientation); + + priv->cell_area_context = gtk_cell_area_create_context (priv->cell_area); + + priv->add_editable_id = + g_signal_connect (priv->cell_area, "add-editable", + G_CALLBACK (gtk_icon_view_add_editable), icon_view); + priv->remove_editable_id = + g_signal_connect (priv->cell_area, "remove-editable", + G_CALLBACK (gtk_icon_view_remove_editable), icon_view); + + update_text_cell (icon_view); + update_pixbuf_cell (icon_view); +} + +static GtkCellArea * +gtk_icon_view_cell_layout_get_area (GtkCellLayout *cell_layout) +{ + GtkIconView *icon_view = GTK_ICON_VIEW (cell_layout); + GtkIconViewPrivate *priv = icon_view->priv; + + if (G_UNLIKELY (!priv->cell_area)) + gtk_icon_view_ensure_cell_area (icon_view, NULL); + + return icon_view->priv->cell_area; +} + +void +_gtk_icon_view_set_cell_data (GtkIconView *icon_view, + GtkIconViewItem *item) +{ + GtkTreeIter iter; + GtkTreePath *path; + + path = gtk_tree_path_new_from_indices (item->index, -1); + if (!gtk_tree_model_get_iter (icon_view->priv->model, &iter, path)) + return; + gtk_tree_path_free (path); + + gtk_cell_area_apply_attributes (icon_view->priv->cell_area, + icon_view->priv->model, + &iter, FALSE, FALSE); +} + + + +/* Public API */ + + +/** + * gtk_icon_view_new: + * + * Creates a new `GtkIconView` widget + * + * Returns: A newly created `GtkIconView` widget + * + * Deprecated: 4.10: Use GtkGridView instead + **/ +GtkWidget * +gtk_icon_view_new (void) +{ + return g_object_new (GTK_TYPE_ICON_VIEW, NULL); +} + +/** + * gtk_icon_view_new_with_area: + * @area: the `GtkCellArea` to use to layout cells + * + * Creates a new `GtkIconView` widget using the + * specified @area to layout cells inside the icons. + * + * Returns: A newly created `GtkIconView` widget + * + * Deprecated: 4.10: Use GtkGridView instead + **/ +GtkWidget * +gtk_icon_view_new_with_area (GtkCellArea *area) +{ + return g_object_new (GTK_TYPE_ICON_VIEW, "cell-area", area, NULL); +} + +/** + * gtk_icon_view_new_with_model: + * @model: The model. + * + * Creates a new `GtkIconView` widget with the model @model. + * + * Returns: A newly created `GtkIconView` widget. + * + * Deprecated: 4.10: Use GtkGridView instead + **/ +GtkWidget * +gtk_icon_view_new_with_model (GtkTreeModel *model) +{ + return g_object_new (GTK_TYPE_ICON_VIEW, "model", model, NULL); +} + +/** + * gtk_icon_view_get_path_at_pos: + * @icon_view: A `GtkIconView`. + * @x: The x position to be identified + * @y: The y position to be identified + * + * Gets the path for the icon at the given position. + * + * Returns: (nullable) (transfer full): The `GtkTreePath` corresponding + * to the icon or %NULL if no icon exists at that position. + * + * Deprecated: 4.10: Use GtkGridView instead + **/ +GtkTreePath * +gtk_icon_view_get_path_at_pos (GtkIconView *icon_view, + int x, + int y) +{ + GtkIconViewItem *item; + GtkTreePath *path; + + g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), NULL); + + item = _gtk_icon_view_get_item_at_coords (icon_view, x, y, TRUE, NULL); + + if (!item) + return NULL; + + path = gtk_tree_path_new_from_indices (item->index, -1); + + return path; +} + +/** + * gtk_icon_view_get_item_at_pos: + * @icon_view: A `GtkIconView`. + * @x: The x position to be identified + * @y: The y position to be identified + * @path: (out) (optional): Return location for the path + * @cell: (out) (optional) (transfer none): Return location for the renderer + * responsible for the cell at (@x, @y) + * + * Gets the path and cell for the icon at the given position. + * + * Returns: %TRUE if an item exists at the specified position + * + * Deprecated: 4.10: Use GtkGridView instead + **/ +gboolean +gtk_icon_view_get_item_at_pos (GtkIconView *icon_view, + int x, + int y, + GtkTreePath **path, + GtkCellRenderer **cell) +{ + GtkIconViewItem *item; + GtkCellRenderer *renderer = NULL; + + g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), FALSE); + + item = _gtk_icon_view_get_item_at_coords (icon_view, x, y, TRUE, &renderer); + + if (path != NULL) + { + if (item != NULL) + *path = gtk_tree_path_new_from_indices (item->index, -1); + else + *path = NULL; + } + + if (cell != NULL) + *cell = renderer; + + return (item != NULL); +} + +/** + * gtk_icon_view_get_cell_rect: + * @icon_view: a `GtkIconView` + * @path: a `GtkTreePath` + * @cell: (nullable): a `GtkCellRenderer` + * @rect: (out): rectangle to fill with cell rect + * + * Fills the bounding rectangle in widget coordinates for the cell specified by + * @path and @cell. If @cell is %NULL the main cell area is used. + * + * This function is only valid if @icon_view is realized. + * + * Returns: %FALSE if there is no such item, %TRUE otherwise + * + * Deprecated: 4.10: Use GtkGridView instead + */ +gboolean +gtk_icon_view_get_cell_rect (GtkIconView *icon_view, + GtkTreePath *path, + GtkCellRenderer *cell, + GdkRectangle *rect) +{ + GtkIconViewItem *item = NULL; + + g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), FALSE); + g_return_val_if_fail (cell == NULL || GTK_IS_CELL_RENDERER (cell), FALSE); + + if (gtk_tree_path_get_depth (path) > 0) + item = g_list_nth_data (icon_view->priv->items, + gtk_tree_path_get_indices(path)[0]); + + if (!item) + return FALSE; + + if (cell) + { + GtkCellAreaContext *context; + + context = g_ptr_array_index (icon_view->priv->row_contexts, item->row); + _gtk_icon_view_set_cell_data (icon_view, item); + gtk_cell_area_get_cell_allocation (icon_view->priv->cell_area, context, + GTK_WIDGET (icon_view), + cell, &item->cell_area, rect); + } + else + { + rect->x = item->cell_area.x - icon_view->priv->item_padding; + rect->y = item->cell_area.y - icon_view->priv->item_padding; + rect->width = item->cell_area.width + icon_view->priv->item_padding * 2; + rect->height = item->cell_area.height + icon_view->priv->item_padding * 2; + } + + return TRUE; +} + +/** + * gtk_icon_view_set_tooltip_item: + * @icon_view: a `GtkIconView` + * @tooltip: a `GtkTooltip` + * @path: a `GtkTreePath` + * + * Sets the tip area of @tooltip to be the area covered by the item at @path. + * See also gtk_icon_view_set_tooltip_column() for a simpler alternative. + * See also gtk_tooltip_set_tip_area(). + * + * Deprecated: 4.10: Use GtkGridView instead + */ +void +gtk_icon_view_set_tooltip_item (GtkIconView *icon_view, + GtkTooltip *tooltip, + GtkTreePath *path) +{ + g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); + g_return_if_fail (GTK_IS_TOOLTIP (tooltip)); + + gtk_icon_view_set_tooltip_cell (icon_view, tooltip, path, NULL); +} + +/** + * gtk_icon_view_set_tooltip_cell: + * @icon_view: a `GtkIconView` + * @tooltip: a `GtkTooltip` + * @path: a `GtkTreePath` + * @cell: (nullable): a `GtkCellRenderer` + * + * Sets the tip area of @tooltip to the area which @cell occupies in + * the item pointed to by @path. See also gtk_tooltip_set_tip_area(). + * + * See also gtk_icon_view_set_tooltip_column() for a simpler alternative. + * + * Deprecated: 4.10: Use GtkGridView instead + */ +void +gtk_icon_view_set_tooltip_cell (GtkIconView *icon_view, + GtkTooltip *tooltip, + GtkTreePath *path, + GtkCellRenderer *cell) +{ + GdkRectangle rect; + + g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); + g_return_if_fail (GTK_IS_TOOLTIP (tooltip)); + g_return_if_fail (cell == NULL || GTK_IS_CELL_RENDERER (cell)); + + if (!gtk_icon_view_get_cell_rect (icon_view, path, cell, &rect)) + return; + + gtk_tooltip_set_tip_area (tooltip, &rect); +} + + +/** + * gtk_icon_view_get_tooltip_context: + * @icon_view: an `GtkIconView` + * @x: the x coordinate (relative to widget coordinates) + * @y: the y coordinate (relative to widget coordinates) + * @keyboard_tip: whether this is a keyboard tooltip or not + * @model: (out) (optional) (transfer none): a pointer to receive a `GtkTreeModel` + * @path: (out) (optional): a pointer to receive a `GtkTreePath` + * @iter: (out) (optional): a pointer to receive a `GtkTreeIter` + * + * This function is supposed to be used in a `GtkWidget::query-tooltip` + * signal handler for `GtkIconView`. The @x, @y and @keyboard_tip values + * which are received in the signal handler, should be passed to this + * function without modification. + * + * The return value indicates whether there is an icon view item at the given + * coordinates (%TRUE) or not (%FALSE) for mouse tooltips. For keyboard + * tooltips the item returned will be the cursor item. When %TRUE, then any of + * @model, @path and @iter which have been provided will be set to point to + * that row and the corresponding model. + * + * Returns: whether or not the given tooltip context points to an item + * + * Deprecated: 4.10: Use GtkGridView instead + */ +gboolean +gtk_icon_view_get_tooltip_context (GtkIconView *icon_view, + int x, + int y, + gboolean keyboard_tip, + GtkTreeModel **model, + GtkTreePath **path, + GtkTreeIter *iter) +{ + GtkTreePath *tmppath = NULL; + + g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), FALSE); + + if (keyboard_tip) + { + gtk_icon_view_get_cursor (icon_view, &tmppath, NULL); + + if (!tmppath) + return FALSE; + } + else + { + if (!gtk_icon_view_get_item_at_pos (icon_view, x, y, &tmppath, NULL)) + return FALSE; + } + + if (model) + *model = gtk_icon_view_get_model (icon_view); + + if (iter) + gtk_tree_model_get_iter (gtk_icon_view_get_model (icon_view), + iter, tmppath); + + if (path) + *path = tmppath; + else + gtk_tree_path_free (tmppath); + + return TRUE; +} + +static gboolean +gtk_icon_view_set_tooltip_query_cb (GtkWidget *widget, + int x, + int y, + gboolean keyboard_tip, + GtkTooltip *tooltip, + gpointer data) +{ + char *str; + GtkTreeIter iter; + GtkTreePath *path; + GtkTreeModel *model; + GtkIconView *icon_view = GTK_ICON_VIEW (widget); + + if (!gtk_icon_view_get_tooltip_context (GTK_ICON_VIEW (widget), + x, y, + keyboard_tip, + &model, &path, &iter)) + return FALSE; + + gtk_tree_model_get (model, &iter, icon_view->priv->tooltip_column, &str, -1); + + if (!str) + { + gtk_tree_path_free (path); + return FALSE; + } + + gtk_tooltip_set_markup (tooltip, str); + gtk_icon_view_set_tooltip_item (icon_view, tooltip, path); + + gtk_tree_path_free (path); + g_free (str); + + return TRUE; +} + + +/** + * gtk_icon_view_set_tooltip_column: + * @icon_view: a `GtkIconView` + * @column: an integer, which is a valid column number for @icon_view’s model + * + * If you only plan to have simple (text-only) tooltips on full items, you + * can use this function to have `GtkIconView` handle these automatically + * for you. @column should be set to the column in @icon_view’s model + * containing the tooltip texts, or -1 to disable this feature. + * + * When enabled, `GtkWidget:has-tooltip` will be set to %TRUE and + * @icon_view will connect a `GtkWidget::query-tooltip` signal handler. + * + * Note that the signal handler sets the text with gtk_tooltip_set_markup(), + * so &, <, etc have to be escaped in the text. + * + * Deprecated: 4.10: Use GtkGridView instead + */ +void +gtk_icon_view_set_tooltip_column (GtkIconView *icon_view, + int column) +{ + g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); + + if (column == icon_view->priv->tooltip_column) + return; + + if (column == -1) + { + g_signal_handlers_disconnect_by_func (icon_view, + gtk_icon_view_set_tooltip_query_cb, + NULL); + gtk_widget_set_has_tooltip (GTK_WIDGET (icon_view), FALSE); + } + else + { + if (icon_view->priv->tooltip_column == -1) + { + g_signal_connect (icon_view, "query-tooltip", + G_CALLBACK (gtk_icon_view_set_tooltip_query_cb), NULL); + gtk_widget_set_has_tooltip (GTK_WIDGET (icon_view), TRUE); + } + } + + icon_view->priv->tooltip_column = column; + g_object_notify (G_OBJECT (icon_view), "tooltip-column"); +} + +/** + * gtk_icon_view_get_tooltip_column: + * @icon_view: a `GtkIconView` + * + * Returns the column of @icon_view’s model which is being used for + * displaying tooltips on @icon_view’s rows. + * + * Returns: the index of the tooltip column that is currently being + * used, or -1 if this is disabled. + * + * Deprecated: 4.10: Use GtkGridView instead + */ +int +gtk_icon_view_get_tooltip_column (GtkIconView *icon_view) +{ + g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), 0); + + return icon_view->priv->tooltip_column; +} + +/** + * gtk_icon_view_get_visible_range: + * @icon_view: A `GtkIconView` + * @start_path: (out) (optional): Return location for start of region + * @end_path: (out) (optional): Return location for end of region + * + * Sets @start_path and @end_path to be the first and last visible path. + * Note that there may be invisible paths in between. + * + * Both paths should be freed with gtk_tree_path_free() after use. + * + * Returns: %TRUE, if valid paths were placed in @start_path and @end_path + * + * Deprecated: 4.10: Use GtkGridView instead + */ +gboolean +gtk_icon_view_get_visible_range (GtkIconView *icon_view, + GtkTreePath **start_path, + GtkTreePath **end_path) +{ + int start_index = -1; + int end_index = -1; + GList *icons; + + g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), FALSE); + + if (icon_view->priv->hadjustment == NULL || + icon_view->priv->vadjustment == NULL) + return FALSE; + + if (start_path == NULL && end_path == NULL) + return FALSE; + + for (icons = icon_view->priv->items; icons; icons = icons->next) + { + GtkIconViewItem *item = icons->data; + GdkRectangle *item_area = &item->cell_area; + + if ((item_area->x + item_area->width >= (int)gtk_adjustment_get_value (icon_view->priv->hadjustment)) && + (item_area->y + item_area->height >= (int)gtk_adjustment_get_value (icon_view->priv->vadjustment)) && + (item_area->x <= + (int) (gtk_adjustment_get_value (icon_view->priv->hadjustment) + + gtk_adjustment_get_page_size (icon_view->priv->hadjustment))) && + (item_area->y <= + (int) (gtk_adjustment_get_value (icon_view->priv->vadjustment) + + gtk_adjustment_get_page_size (icon_view->priv->vadjustment)))) + { + if (start_index == -1) + start_index = item->index; + end_index = item->index; + } + } + + if (start_path && start_index != -1) + *start_path = gtk_tree_path_new_from_indices (start_index, -1); + if (end_path && end_index != -1) + *end_path = gtk_tree_path_new_from_indices (end_index, -1); + + return start_index != -1; +} + +/** + * gtk_icon_view_selected_foreach: + * @icon_view: A `GtkIconView`. + * @func: (scope call): The function to call for each selected icon. + * @data: User data to pass to the function. + * + * Calls a function for each selected icon. Note that the model or + * selection cannot be modified from within this function. + * + * Deprecated: 4.10: Use GtkGridView instead + **/ +void +gtk_icon_view_selected_foreach (GtkIconView *icon_view, + GtkIconViewForeachFunc func, + gpointer data) +{ + GList *list; + + for (list = icon_view->priv->items; list; list = list->next) + { + GtkIconViewItem *item = list->data; + GtkTreePath *path = gtk_tree_path_new_from_indices (item->index, -1); + + if (item->selected) + (* func) (icon_view, path, data); + + gtk_tree_path_free (path); + } +} + +/** + * gtk_icon_view_set_selection_mode: + * @icon_view: A `GtkIconView`. + * @mode: The selection mode + * + * Sets the selection mode of the @icon_view. + * + * Deprecated: 4.10: Use GtkGridView instead + **/ +void +gtk_icon_view_set_selection_mode (GtkIconView *icon_view, + GtkSelectionMode mode) +{ + g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); + + if (mode == icon_view->priv->selection_mode) + return; + + if (mode == GTK_SELECTION_NONE || + icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE) + gtk_icon_view_unselect_all (icon_view); + + icon_view->priv->selection_mode = mode; + + g_object_notify (G_OBJECT (icon_view), "selection-mode"); +} + +/** + * gtk_icon_view_get_selection_mode: + * @icon_view: A `GtkIconView`. + * + * Gets the selection mode of the @icon_view. + * + * Returns: the current selection mode + * + * Deprecated: 4.10: Use GtkGridView instead + **/ +GtkSelectionMode +gtk_icon_view_get_selection_mode (GtkIconView *icon_view) +{ + g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), GTK_SELECTION_SINGLE); + + return icon_view->priv->selection_mode; +} + +/** + * gtk_icon_view_set_model: + * @icon_view: A `GtkIconView`. + * @model: (nullable): The model. + * + * Sets the model for a `GtkIconView`. + * If the @icon_view already has a model set, it will remove + * it before setting the new model. If @model is %NULL, then + * it will unset the old model. + * + * Deprecated: 4.10: Use GtkGridView instead + **/ +void +gtk_icon_view_set_model (GtkIconView *icon_view, + GtkTreeModel *model) +{ + gboolean dirty; + + g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); + g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model)); + + if (icon_view->priv->model == model) + return; + + if (icon_view->priv->scroll_to_path) + { + gtk_tree_row_reference_free (icon_view->priv->scroll_to_path); + icon_view->priv->scroll_to_path = NULL; + } + + /* The area can be NULL while disposing */ + if (icon_view->priv->cell_area) + gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); + + dirty = gtk_icon_view_unselect_all_internal (icon_view); + + if (model) + { + if (icon_view->priv->pixbuf_column != -1) + { + g_return_if_fail (gtk_tree_model_get_column_type (model, icon_view->priv->pixbuf_column) == GDK_TYPE_PIXBUF); + } + + if (icon_view->priv->text_column != -1) + { + g_return_if_fail (gtk_tree_model_get_column_type (model, icon_view->priv->text_column) == G_TYPE_STRING); + } + + if (icon_view->priv->markup_column != -1) + { + g_return_if_fail (gtk_tree_model_get_column_type (model, icon_view->priv->markup_column) == G_TYPE_STRING); + } + + } + + if (icon_view->priv->model) + { + g_signal_handlers_disconnect_by_func (icon_view->priv->model, + gtk_icon_view_row_changed, + icon_view); + g_signal_handlers_disconnect_by_func (icon_view->priv->model, + gtk_icon_view_row_inserted, + icon_view); + g_signal_handlers_disconnect_by_func (icon_view->priv->model, + gtk_icon_view_row_deleted, + icon_view); + g_signal_handlers_disconnect_by_func (icon_view->priv->model, + gtk_icon_view_rows_reordered, + icon_view); + + g_object_unref (icon_view->priv->model); + + g_list_free_full (icon_view->priv->items, (GDestroyNotify) gtk_icon_view_item_free); + icon_view->priv->items = NULL; + icon_view->priv->anchor_item = NULL; + icon_view->priv->cursor_item = NULL; + icon_view->priv->last_single_clicked = NULL; + icon_view->priv->last_prelight = NULL; + icon_view->priv->width = 0; + icon_view->priv->height = 0; + } + + icon_view->priv->model = model; + + if (icon_view->priv->model) + { + g_object_ref (icon_view->priv->model); + g_signal_connect (icon_view->priv->model, + "row-changed", + G_CALLBACK (gtk_icon_view_row_changed), + icon_view); + g_signal_connect (icon_view->priv->model, + "row-inserted", + G_CALLBACK (gtk_icon_view_row_inserted), + icon_view); + g_signal_connect (icon_view->priv->model, + "row-deleted", + G_CALLBACK (gtk_icon_view_row_deleted), + icon_view); + g_signal_connect (icon_view->priv->model, + "rows-reordered", + G_CALLBACK (gtk_icon_view_rows_reordered), + icon_view); + + gtk_icon_view_build_items (icon_view); + } + + g_object_notify (G_OBJECT (icon_view), "model"); + + if (dirty) + g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0); + + gtk_widget_queue_resize (GTK_WIDGET (icon_view)); +} + +/** + * gtk_icon_view_get_model: + * @icon_view: a `GtkIconView` + * + * Returns the model the `GtkIconView` is based on. Returns %NULL if the + * model is unset. + * + * Returns: (nullable) (transfer none): The currently used `GtkTreeModel` + * + * Deprecated: 4.10: Use GtkGridView instead + */ +GtkTreeModel * +gtk_icon_view_get_model (GtkIconView *icon_view) +{ + g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), NULL); + + return icon_view->priv->model; +} + +static void +update_text_cell (GtkIconView *icon_view) +{ + if (!icon_view->priv->cell_area) + return; + + if (icon_view->priv->text_column == -1 && + icon_view->priv->markup_column == -1) + { + if (icon_view->priv->text_cell != NULL) + { + gtk_cell_area_remove (icon_view->priv->cell_area, + icon_view->priv->text_cell); + icon_view->priv->text_cell = NULL; + } + } + else + { + if (icon_view->priv->text_cell == NULL) + { + icon_view->priv->text_cell = gtk_cell_renderer_text_new (); + + gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (icon_view), icon_view->priv->text_cell, FALSE); + } + + if (icon_view->priv->markup_column != -1) + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (icon_view), + icon_view->priv->text_cell, + "markup", icon_view->priv->markup_column, + NULL); + else + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (icon_view), + icon_view->priv->text_cell, + "text", icon_view->priv->text_column, + NULL); + + if (icon_view->priv->item_orientation == GTK_ORIENTATION_VERTICAL) + g_object_set (icon_view->priv->text_cell, + "alignment", PANGO_ALIGN_CENTER, + "wrap-mode", PANGO_WRAP_WORD_CHAR, + "xalign", 0.5, + "yalign", 0.0, + NULL); + else + g_object_set (icon_view->priv->text_cell, + "alignment", PANGO_ALIGN_LEFT, + "wrap-mode", PANGO_WRAP_WORD_CHAR, + "xalign", 0.0, + "yalign", 0.5, + NULL); + } +} + +static void +update_pixbuf_cell (GtkIconView *icon_view) +{ + if (!icon_view->priv->cell_area) + return; + + if (icon_view->priv->pixbuf_column == -1) + { + if (icon_view->priv->pixbuf_cell != NULL) + { + gtk_cell_area_remove (icon_view->priv->cell_area, + icon_view->priv->pixbuf_cell); + + icon_view->priv->pixbuf_cell = NULL; + } + } + else + { + if (icon_view->priv->pixbuf_cell == NULL) + { + icon_view->priv->pixbuf_cell = gtk_cell_renderer_pixbuf_new (); + + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (icon_view), icon_view->priv->pixbuf_cell, FALSE); + } + + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (icon_view), + icon_view->priv->pixbuf_cell, + "pixbuf", icon_view->priv->pixbuf_column, + NULL); + + if (icon_view->priv->item_orientation == GTK_ORIENTATION_VERTICAL) + g_object_set (icon_view->priv->pixbuf_cell, + "xalign", 0.5, + "yalign", 1.0, + NULL); + else + g_object_set (icon_view->priv->pixbuf_cell, + "xalign", 0.0, + "yalign", 0.0, + NULL); + } +} + +/** + * gtk_icon_view_set_text_column: + * @icon_view: A `GtkIconView`. + * @column: A column in the currently used model, or -1 to display no text + * + * Sets the column with text for @icon_view to be @column. The text + * column must be of type `G_TYPE_STRING`. + * + * Deprecated: 4.10: Use GtkGridView instead + **/ +void +gtk_icon_view_set_text_column (GtkIconView *icon_view, + int column) +{ + if (column == icon_view->priv->text_column) + return; + + if (column == -1) + icon_view->priv->text_column = -1; + else + { + if (icon_view->priv->model != NULL) + { + g_return_if_fail (gtk_tree_model_get_column_type (icon_view->priv->model, column) == G_TYPE_STRING); + } + + icon_view->priv->text_column = column; + } + + if (icon_view->priv->cell_area) + gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); + + update_text_cell (icon_view); + + gtk_icon_view_invalidate_sizes (icon_view); + + g_object_notify (G_OBJECT (icon_view), "text-column"); +} + +/** + * gtk_icon_view_get_text_column: + * @icon_view: A `GtkIconView`. + * + * Returns the column with text for @icon_view. + * + * Returns: the text column, or -1 if it’s unset. + * + * Deprecated: 4.10: Use GtkGridView instead + */ +int +gtk_icon_view_get_text_column (GtkIconView *icon_view) +{ + g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), -1); + + return icon_view->priv->text_column; +} + +/** + * gtk_icon_view_set_markup_column: + * @icon_view: A `GtkIconView`. + * @column: A column in the currently used model, or -1 to display no text + * + * Sets the column with markup information for @icon_view to be + * @column. The markup column must be of type `G_TYPE_STRING`. + * If the markup column is set to something, it overrides + * the text column set by gtk_icon_view_set_text_column(). + * + * Deprecated: 4.10: Use GtkGridView instead + **/ +void +gtk_icon_view_set_markup_column (GtkIconView *icon_view, + int column) +{ + if (column == icon_view->priv->markup_column) + return; + + if (column == -1) + icon_view->priv->markup_column = -1; + else + { + if (icon_view->priv->model != NULL) + { + g_return_if_fail (gtk_tree_model_get_column_type (icon_view->priv->model, column) == G_TYPE_STRING); + } + + icon_view->priv->markup_column = column; + } + + if (icon_view->priv->cell_area) + gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); + + update_text_cell (icon_view); + + gtk_icon_view_invalidate_sizes (icon_view); + + g_object_notify (G_OBJECT (icon_view), "markup-column"); +} + +/** + * gtk_icon_view_get_markup_column: + * @icon_view: A `GtkIconView`. + * + * Returns the column with markup text for @icon_view. + * + * Returns: the markup column, or -1 if it’s unset. + * + * Deprecated: 4.10: Use GtkGridView instead + */ +int +gtk_icon_view_get_markup_column (GtkIconView *icon_view) +{ + g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), -1); + + return icon_view->priv->markup_column; +} + +/** + * gtk_icon_view_set_pixbuf_column: + * @icon_view: A `GtkIconView`. + * @column: A column in the currently used model, or -1 to disable + * + * Sets the column with pixbufs for @icon_view to be @column. The pixbuf + * column must be of type `GDK_TYPE_PIXBUF` + * + * Deprecated: 4.10: Use GtkGridView instead + **/ +void +gtk_icon_view_set_pixbuf_column (GtkIconView *icon_view, + int column) +{ + if (column == icon_view->priv->pixbuf_column) + return; + + if (column == -1) + icon_view->priv->pixbuf_column = -1; + else + { + if (icon_view->priv->model != NULL) + { + g_return_if_fail (gtk_tree_model_get_column_type (icon_view->priv->model, column) == GDK_TYPE_PIXBUF); + } + + icon_view->priv->pixbuf_column = column; + } + + if (icon_view->priv->cell_area) + gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); + + update_pixbuf_cell (icon_view); + + gtk_icon_view_invalidate_sizes (icon_view); + + g_object_notify (G_OBJECT (icon_view), "pixbuf-column"); + +} + +/** + * gtk_icon_view_get_pixbuf_column: + * @icon_view: A `GtkIconView`. + * + * Returns the column with pixbufs for @icon_view. + * + * Returns: the pixbuf column, or -1 if it’s unset. + * + * Deprecated: 4.10: Use GtkGridView instead + */ +int +gtk_icon_view_get_pixbuf_column (GtkIconView *icon_view) +{ + g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), -1); + + return icon_view->priv->pixbuf_column; +} + +/** + * gtk_icon_view_select_path: + * @icon_view: A `GtkIconView`. + * @path: The `GtkTreePath` to be selected. + * + * Selects the row at @path. + * + * Deprecated: 4.10: Use GtkGridView instead + **/ +void +gtk_icon_view_select_path (GtkIconView *icon_view, + GtkTreePath *path) +{ + GtkIconViewItem *item = NULL; + + g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); + g_return_if_fail (icon_view->priv->model != NULL); + g_return_if_fail (path != NULL); + + if (gtk_tree_path_get_depth (path) > 0) + item = g_list_nth_data (icon_view->priv->items, + gtk_tree_path_get_indices(path)[0]); + + if (item) + _gtk_icon_view_select_item (icon_view, item); +} + +/** + * gtk_icon_view_unselect_path: + * @icon_view: A `GtkIconView`. + * @path: The `GtkTreePath` to be unselected. + * + * Unselects the row at @path. + * + * Deprecated: 4.10: Use GtkGridView instead + **/ +void +gtk_icon_view_unselect_path (GtkIconView *icon_view, + GtkTreePath *path) +{ + GtkIconViewItem *item; + + g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); + g_return_if_fail (icon_view->priv->model != NULL); + g_return_if_fail (path != NULL); + + item = g_list_nth_data (icon_view->priv->items, + gtk_tree_path_get_indices(path)[0]); + + if (!item) + return; + + _gtk_icon_view_unselect_item (icon_view, item); +} + +/** + * gtk_icon_view_get_selected_items: + * @icon_view: A `GtkIconView`. + * + * Creates a list of paths of all selected items. Additionally, if you are + * planning on modifying the model after calling this function, you may + * want to convert the returned list into a list of `GtkTreeRowReferences`. + * To do this, you can use gtk_tree_row_reference_new(). + * + * To free the return value, use `g_list_free_full`: + * |[ + * GtkWidget *icon_view = gtk_icon_view_new (); + * // Use icon_view + * + * GList *list = gtk_icon_view_get_selected_items (GTK_ICON_VIEW (icon_view)); + * + * // use list + * + * g_list_free_full (list, (GDestroyNotify) gtk_tree_path_free); + * ]| + * + * Returns: (element-type GtkTreePath) (transfer full): A `GList` containing a `GtkTreePath` for each selected row. + * + * Deprecated: 4.10: Use GtkGridView instead + **/ +GList * +gtk_icon_view_get_selected_items (GtkIconView *icon_view) +{ + GList *list; + GList *selected = NULL; + + g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), NULL); + + for (list = icon_view->priv->items; list != NULL; list = list->next) + { + GtkIconViewItem *item = list->data; + + if (item->selected) + { + GtkTreePath *path = gtk_tree_path_new_from_indices (item->index, -1); + + selected = g_list_prepend (selected, path); + } + } + + return selected; +} + +/** + * gtk_icon_view_select_all: + * @icon_view: A `GtkIconView`. + * + * Selects all the icons. @icon_view must has its selection mode set + * to %GTK_SELECTION_MULTIPLE. + * + * Deprecated: 4.10: Use GtkGridView instead + **/ +void +gtk_icon_view_select_all (GtkIconView *icon_view) +{ + GList *items; + gboolean dirty = FALSE; + + g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); + + if (icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE) + return; + + for (items = icon_view->priv->items; items; items = items->next) + { + GtkIconViewItem *item = items->data; + + if (!item->selected) + { + dirty = TRUE; + item->selected = TRUE; + gtk_icon_view_queue_draw_item (icon_view, item); + } + } + + if (dirty) + g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0); +} + +/** + * gtk_icon_view_unselect_all: + * @icon_view: A `GtkIconView`. + * + * Unselects all the icons. + * + * Deprecated: 4.10: Use GtkGridView instead + **/ +void +gtk_icon_view_unselect_all (GtkIconView *icon_view) +{ + gboolean dirty = FALSE; + + g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); + + if (icon_view->priv->selection_mode == GTK_SELECTION_BROWSE) + return; + + dirty = gtk_icon_view_unselect_all_internal (icon_view); + + if (dirty) + g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0); +} + +/** + * gtk_icon_view_path_is_selected: + * @icon_view: A `GtkIconView`. + * @path: A `GtkTreePath` to check selection on. + * + * Returns %TRUE if the icon pointed to by @path is currently + * selected. If @path does not point to a valid location, %FALSE is returned. + * + * Returns: %TRUE if @path is selected. + * + * Deprecated: 4.10: Use GtkGridView instead + **/ +gboolean +gtk_icon_view_path_is_selected (GtkIconView *icon_view, + GtkTreePath *path) +{ + GtkIconViewItem *item; + + g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), FALSE); + g_return_val_if_fail (icon_view->priv->model != NULL, FALSE); + g_return_val_if_fail (path != NULL, FALSE); + + item = g_list_nth_data (icon_view->priv->items, + gtk_tree_path_get_indices(path)[0]); + + if (!item) + return FALSE; + + return item->selected; +} + +/** + * gtk_icon_view_get_item_row: + * @icon_view: a `GtkIconView` + * @path: the `GtkTreePath` of the item + * + * Gets the row in which the item @path is currently + * displayed. Row numbers start at 0. + * + * Returns: The row in which the item is displayed + * + * Deprecated: 4.10: Use GtkGridView instead + */ +int +gtk_icon_view_get_item_row (GtkIconView *icon_view, + GtkTreePath *path) +{ + GtkIconViewItem *item; + + g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), -1); + g_return_val_if_fail (icon_view->priv->model != NULL, -1); + g_return_val_if_fail (path != NULL, -1); + + item = g_list_nth_data (icon_view->priv->items, + gtk_tree_path_get_indices(path)[0]); + + if (!item) + return -1; + + return item->row; +} + +/** + * gtk_icon_view_get_item_column: + * @icon_view: a `GtkIconView` + * @path: the `GtkTreePath` of the item + * + * Gets the column in which the item @path is currently + * displayed. Column numbers start at 0. + * + * Returns: The column in which the item is displayed + * + * Deprecated: 4.10: Use GtkGridView instead + */ +int +gtk_icon_view_get_item_column (GtkIconView *icon_view, + GtkTreePath *path) +{ + GtkIconViewItem *item; + + g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), -1); + g_return_val_if_fail (icon_view->priv->model != NULL, -1); + g_return_val_if_fail (path != NULL, -1); + + item = g_list_nth_data (icon_view->priv->items, + gtk_tree_path_get_indices(path)[0]); + + if (!item) + return -1; + + return item->col; +} + +/** + * gtk_icon_view_item_activated: + * @icon_view: A `GtkIconView` + * @path: The `GtkTreePath` to be activated + * + * Activates the item determined by @path. + * + * Deprecated: 4.10: Use GtkGridView instead + **/ +void +gtk_icon_view_item_activated (GtkIconView *icon_view, + GtkTreePath *path) +{ + g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); + g_return_if_fail (path != NULL); + + g_signal_emit (icon_view, icon_view_signals[ITEM_ACTIVATED], 0, path); +} + +/** + * gtk_icon_view_set_item_orientation: + * @icon_view: a `GtkIconView` + * @orientation: the relative position of texts and icons + * + * Sets the ::item-orientation property which determines whether the labels + * are drawn beside the icons instead of below. + * + * Deprecated: 4.10: Use GtkGridView instead + **/ +void +gtk_icon_view_set_item_orientation (GtkIconView *icon_view, + GtkOrientation orientation) +{ + g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); + + if (icon_view->priv->item_orientation != orientation) + { + icon_view->priv->item_orientation = orientation; + + if (icon_view->priv->cell_area) + { + if (GTK_IS_ORIENTABLE (icon_view->priv->cell_area)) + gtk_orientable_set_orientation (GTK_ORIENTABLE (icon_view->priv->cell_area), + icon_view->priv->item_orientation); + + gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); + } + + gtk_icon_view_invalidate_sizes (icon_view); + + update_text_cell (icon_view); + update_pixbuf_cell (icon_view); + + g_object_notify (G_OBJECT (icon_view), "item-orientation"); + } +} + +/** + * gtk_icon_view_get_item_orientation: + * @icon_view: a `GtkIconView` + * + * Returns the value of the ::item-orientation property which determines + * whether the labels are drawn beside the icons instead of below. + * + * Returns: the relative position of texts and icons + * + * Deprecated: 4.10: Use GtkGridView instead + **/ +GtkOrientation +gtk_icon_view_get_item_orientation (GtkIconView *icon_view) +{ + g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), + GTK_ORIENTATION_VERTICAL); + + return icon_view->priv->item_orientation; +} + +/** + * gtk_icon_view_set_columns: + * @icon_view: a `GtkIconView` + * @columns: the number of columns + * + * Sets the ::columns property which determines in how + * many columns the icons are arranged. If @columns is + * -1, the number of columns will be chosen automatically + * to fill the available area. + * + * Deprecated: 4.10: Use GtkGridView instead + */ +void +gtk_icon_view_set_columns (GtkIconView *icon_view, + int columns) +{ + g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); + + if (icon_view->priv->columns != columns) + { + icon_view->priv->columns = columns; + + if (icon_view->priv->cell_area) + gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); + + gtk_widget_queue_resize (GTK_WIDGET (icon_view)); + + g_object_notify (G_OBJECT (icon_view), "columns"); + } +} + +/** + * gtk_icon_view_get_columns: + * @icon_view: a `GtkIconView` + * + * Returns the value of the ::columns property. + * + * Returns: the number of columns, or -1 + * + * Deprecated: 4.10: Use GtkGridView instead + */ +int +gtk_icon_view_get_columns (GtkIconView *icon_view) +{ + g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), -1); + + return icon_view->priv->columns; +} + +/** + * gtk_icon_view_set_item_width: + * @icon_view: a `GtkIconView` + * @item_width: the width for each item + * + * Sets the ::item-width property which specifies the width + * to use for each item. If it is set to -1, the icon view will + * automatically determine a suitable item size. + * + * Deprecated: 4.10: Use GtkGridView instead + */ +void +gtk_icon_view_set_item_width (GtkIconView *icon_view, + int item_width) +{ + g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); + + if (icon_view->priv->item_width != item_width) + { + icon_view->priv->item_width = item_width; + + if (icon_view->priv->cell_area) + gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); + + gtk_icon_view_invalidate_sizes (icon_view); + + update_text_cell (icon_view); + + g_object_notify (G_OBJECT (icon_view), "item-width"); + } +} + +/** + * gtk_icon_view_get_item_width: + * @icon_view: a `GtkIconView` + * + * Returns the value of the ::item-width property. + * + * Returns: the width of a single item, or -1 + * + * Deprecated: 4.10: Use GtkGridView instead + */ +int +gtk_icon_view_get_item_width (GtkIconView *icon_view) +{ + g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), -1); + + return icon_view->priv->item_width; +} + + +/** + * gtk_icon_view_set_spacing: + * @icon_view: a `GtkIconView` + * @spacing: the spacing + * + * Sets the ::spacing property which specifies the space + * which is inserted between the cells (i.e. the icon and + * the text) of an item. + * + * Deprecated: 4.10: Use GtkGridView instead + */ +void +gtk_icon_view_set_spacing (GtkIconView *icon_view, + int spacing) +{ + g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); + + if (icon_view->priv->spacing != spacing) + { + icon_view->priv->spacing = spacing; + + if (icon_view->priv->cell_area) + gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); + + gtk_icon_view_invalidate_sizes (icon_view); + + g_object_notify (G_OBJECT (icon_view), "spacing"); + } +} + +/** + * gtk_icon_view_get_spacing: + * @icon_view: a `GtkIconView` + * + * Returns the value of the ::spacing property. + * + * Returns: the space between cells + * + * Deprecated: 4.10: Use GtkGridView instead + */ +int +gtk_icon_view_get_spacing (GtkIconView *icon_view) +{ + g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), -1); + + return icon_view->priv->spacing; +} + +/** + * gtk_icon_view_set_row_spacing: + * @icon_view: a `GtkIconView` + * @row_spacing: the row spacing + * + * Sets the ::row-spacing property which specifies the space + * which is inserted between the rows of the icon view. + * + * Deprecated: 4.10: Use GtkGridView instead + */ +void +gtk_icon_view_set_row_spacing (GtkIconView *icon_view, + int row_spacing) +{ + g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); + + if (icon_view->priv->row_spacing != row_spacing) + { + icon_view->priv->row_spacing = row_spacing; + + if (icon_view->priv->cell_area) + gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); + + gtk_icon_view_invalidate_sizes (icon_view); + + g_object_notify (G_OBJECT (icon_view), "row-spacing"); + } +} + +/** + * gtk_icon_view_get_row_spacing: + * @icon_view: a `GtkIconView` + * + * Returns the value of the ::row-spacing property. + * + * Returns: the space between rows + * + * Deprecated: 4.10: Use GtkGridView instead + */ +int +gtk_icon_view_get_row_spacing (GtkIconView *icon_view) +{ + g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), -1); + + return icon_view->priv->row_spacing; +} + +/** + * gtk_icon_view_set_column_spacing: + * @icon_view: a `GtkIconView` + * @column_spacing: the column spacing + * + * Sets the ::column-spacing property which specifies the space + * which is inserted between the columns of the icon view. + * + * Deprecated: 4.10: Use GtkGridView instead + */ +void +gtk_icon_view_set_column_spacing (GtkIconView *icon_view, + int column_spacing) +{ + g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); + + if (icon_view->priv->column_spacing != column_spacing) + { + icon_view->priv->column_spacing = column_spacing; + + if (icon_view->priv->cell_area) + gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); + + gtk_icon_view_invalidate_sizes (icon_view); + + g_object_notify (G_OBJECT (icon_view), "column-spacing"); + } +} + +/** + * gtk_icon_view_get_column_spacing: + * @icon_view: a `GtkIconView` + * + * Returns the value of the ::column-spacing property. + * + * Returns: the space between columns + * + * Deprecated: 4.10: Use GtkGridView instead + */ +int +gtk_icon_view_get_column_spacing (GtkIconView *icon_view) +{ + g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), -1); + + return icon_view->priv->column_spacing; +} + +/** + * gtk_icon_view_set_margin: + * @icon_view: a `GtkIconView` + * @margin: the margin + * + * Sets the ::margin property which specifies the space + * which is inserted at the top, bottom, left and right + * of the icon view. + * + * Deprecated: 4.10: Use GtkGridView instead + */ +void +gtk_icon_view_set_margin (GtkIconView *icon_view, + int margin) +{ + g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); + + if (icon_view->priv->margin != margin) + { + icon_view->priv->margin = margin; + + if (icon_view->priv->cell_area) + gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); + + gtk_icon_view_invalidate_sizes (icon_view); + + g_object_notify (G_OBJECT (icon_view), "margin"); + } +} + +/** + * gtk_icon_view_get_margin: + * @icon_view: a `GtkIconView` + * + * Returns the value of the ::margin property. + * + * Returns: the space at the borders + * + * Deprecated: 4.10: Use GtkGridView instead + */ +int +gtk_icon_view_get_margin (GtkIconView *icon_view) +{ + g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), -1); + + return icon_view->priv->margin; +} + +/** + * gtk_icon_view_set_item_padding: + * @icon_view: a `GtkIconView` + * @item_padding: the item padding + * + * Sets the `GtkIconView`:item-padding property which specifies the padding + * around each of the icon view’s items. + * + * Deprecated: 4.10: Use GtkGridView instead + */ +void +gtk_icon_view_set_item_padding (GtkIconView *icon_view, + int item_padding) +{ + g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); + + if (icon_view->priv->item_padding != item_padding) + { + icon_view->priv->item_padding = item_padding; + + if (icon_view->priv->cell_area) + gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); + + gtk_icon_view_invalidate_sizes (icon_view); + + g_object_notify (G_OBJECT (icon_view), "item-padding"); + } +} + +/** + * gtk_icon_view_get_item_padding: + * @icon_view: a `GtkIconView` + * + * Returns the value of the ::item-padding property. + * + * Returns: the padding around items + * + * Deprecated: 4.10: Use GtkGridView instead + */ +int +gtk_icon_view_get_item_padding (GtkIconView *icon_view) +{ + g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), -1); + + return icon_view->priv->item_padding; +} + +/* Get/set whether drag_motion requested the drag data and + * drag_data_received should thus not actually insert the data, + * since the data doesn’t result from a drop. + */ +static void +set_status_pending (GdkDrop *drop, + GdkDragAction suggested_action) +{ + g_object_set_data (G_OBJECT (drop), + I_("gtk-icon-view-status-pending"), + GINT_TO_POINTER (suggested_action)); +} + +static GdkDragAction +get_status_pending (GdkDrop *drop) +{ + return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (drop), + "gtk-icon-view-status-pending")); +} + +static void +unset_reorderable (GtkIconView *icon_view) +{ + if (icon_view->priv->reorderable) + { + icon_view->priv->reorderable = FALSE; + g_object_notify (G_OBJECT (icon_view), "reorderable"); + } +} + +typedef struct +{ + GtkTreeRowReference *dest_row; + gboolean empty_view_drop; + gboolean drop_append_mode; +} DestRow; + +static void +dest_row_free (gpointer data) +{ + DestRow *dr = (DestRow *)data; + + gtk_tree_row_reference_free (dr->dest_row); + g_free (dr); +} + +static void +set_dest_row (GdkDrop *drop, + GtkTreeModel *model, + GtkTreePath *dest_row, + gboolean empty_view_drop, + gboolean drop_append_mode) +{ + DestRow *dr; + + if (!dest_row) + { + g_object_set_data_full (G_OBJECT (drop), + I_("gtk-icon-view-dest-row"), + NULL, NULL); + return; + } + + dr = g_new0 (DestRow, 1); + + dr->dest_row = gtk_tree_row_reference_new (model, dest_row); + dr->empty_view_drop = empty_view_drop; + dr->drop_append_mode = drop_append_mode; + g_object_set_data_full (G_OBJECT (drop), + I_("gtk-icon-view-dest-row"), + dr, (GDestroyNotify) dest_row_free); +} + +static GtkTreePath* +get_dest_row (GdkDrop *drop) +{ + DestRow *dr; + + dr = g_object_get_data (G_OBJECT (drop), "gtk-icon-view-dest-row"); + + if (dr) + { + GtkTreePath *path = NULL; + + if (dr->dest_row) + path = gtk_tree_row_reference_get_path (dr->dest_row); + else if (dr->empty_view_drop) + path = gtk_tree_path_new_from_indices (0, -1); + else + path = NULL; + + if (path && dr->drop_append_mode) + gtk_tree_path_next (path); + + return path; + } + else + return NULL; +} + +static gboolean +check_model_dnd (GtkTreeModel *model, + GType required_iface, + const char *signal) +{ + if (model == NULL || !G_TYPE_CHECK_INSTANCE_TYPE ((model), required_iface)) + { + g_warning ("You must override the default '%s' handler " + "on GtkIconView when using models that don't support " + "the %s interface and enabling drag-and-drop. The simplest way to do this " + "is to connect to '%s' and call " + "g_signal_stop_emission_by_name() in your signal handler to prevent " + "the default handler from running. Look at the source code " + "for the default handler in gtkiconview.c to get an idea what " + "your handler should do. (gtkiconview.c is in the GTK source " + "code.) If you're using GTK from a language other than C, " + "there may be a more natural way to override default handlers, e.g. via derivation.", + signal, g_type_name (required_iface), signal); + return FALSE; + } + else + return TRUE; +} + +static void +remove_scroll_timeout (GtkIconView *icon_view) +{ + if (icon_view->priv->scroll_timeout_id != 0) + { + g_source_remove (icon_view->priv->scroll_timeout_id); + + icon_view->priv->scroll_timeout_id = 0; + } +} + +static void +gtk_icon_view_autoscroll (GtkIconView *icon_view) +{ + int px, py, width, height; + int hoffset, voffset; + + px = icon_view->priv->event_last_x; + py = icon_view->priv->event_last_y; + + width = gtk_widget_get_width (GTK_WIDGET (icon_view)); + height = gtk_widget_get_height (GTK_WIDGET (icon_view)); + + /* see if we are near the edge. */ + voffset = py - 2 * SCROLL_EDGE_SIZE; + if (voffset > 0) + voffset = MAX (py - (height - 2 * SCROLL_EDGE_SIZE), 0); + + hoffset = px - 2 * SCROLL_EDGE_SIZE; + if (hoffset > 0) + hoffset = MAX (px - (width - 2 * SCROLL_EDGE_SIZE), 0); + + if (voffset != 0) + gtk_adjustment_set_value (icon_view->priv->vadjustment, + gtk_adjustment_get_value (icon_view->priv->vadjustment) + voffset); + + if (hoffset != 0) + gtk_adjustment_set_value (icon_view->priv->hadjustment, + gtk_adjustment_get_value (icon_view->priv->hadjustment) + hoffset); +} + +static gboolean +drag_scroll_timeout (gpointer data) +{ + gtk_icon_view_autoscroll (data); + + return TRUE; +} + +static GdkDragAction +gtk_icon_view_get_action (GtkWidget *widget, + GdkDrop *drop) +{ + GtkIconView *iconview = GTK_ICON_VIEW (widget); + GdkDrag *drag = gdk_drop_get_drag (drop); + GdkDragAction actions; + + actions = gdk_drop_get_actions (drop); + + if (drag == iconview->priv->drag && + actions & GDK_ACTION_MOVE) + return GDK_ACTION_MOVE; + + if (actions & GDK_ACTION_COPY) + return GDK_ACTION_COPY; + + if (actions & GDK_ACTION_MOVE) + return GDK_ACTION_MOVE; + + if (actions & GDK_ACTION_LINK) + return GDK_ACTION_LINK; + + return 0; +} + +static gboolean +set_destination (GtkIconView *icon_view, + GdkDrop *drop, + GtkDropTargetAsync *dest, + int x, + int y, + GdkDragAction *suggested_action, + GType *target) +{ + GtkWidget *widget; + GtkTreePath *path = NULL; + GtkIconViewDropPosition pos; + GtkIconViewDropPosition old_pos; + GtkTreePath *old_dest_path = NULL; + GdkContentFormats *formats; + gboolean can_drop = FALSE; + + widget = GTK_WIDGET (icon_view); + + *suggested_action = 0; + *target = G_TYPE_INVALID; + + if (!icon_view->priv->dest_set) + { + /* someone unset us as a drag dest, note that if + * we return FALSE drag_leave isn't called + */ + + gtk_icon_view_set_drag_dest_item (icon_view, + NULL, + GTK_ICON_VIEW_DROP_LEFT); + + remove_scroll_timeout (GTK_ICON_VIEW (widget)); + + return FALSE; /* no longer a drop site */ + } + + formats = gtk_drop_target_async_get_formats (dest); + *target = gdk_content_formats_match_gtype (formats, formats); + if (*target == G_TYPE_INVALID) + return FALSE; + + if (!gtk_icon_view_get_dest_item_at_pos (icon_view, x, y, &path, &pos)) + { + int n_children; + GtkTreeModel *model; + + /* the row got dropped on empty space, let's setup a special case + */ + + if (path) + gtk_tree_path_free (path); + + model = gtk_icon_view_get_model (icon_view); + + n_children = gtk_tree_model_iter_n_children (model, NULL); + if (n_children) + { + pos = GTK_ICON_VIEW_DROP_BELOW; + path = gtk_tree_path_new_from_indices (n_children - 1, -1); + } + else + { + pos = GTK_ICON_VIEW_DROP_ABOVE; + path = gtk_tree_path_new_from_indices (0, -1); + } + + can_drop = TRUE; + + goto out; + } + + g_assert (path); + + gtk_icon_view_get_drag_dest_item (icon_view, + &old_dest_path, + &old_pos); + + if (old_dest_path) + gtk_tree_path_free (old_dest_path); + + if (TRUE /* FIXME if the location droppable predicate */) + { + can_drop = TRUE; + } + +out: + if (can_drop) + { + *suggested_action = gtk_icon_view_get_action (widget, drop); + + gtk_icon_view_set_drag_dest_item (GTK_ICON_VIEW (widget), + path, pos); + } + else + { + /* can't drop here */ + gtk_icon_view_set_drag_dest_item (GTK_ICON_VIEW (widget), + NULL, + GTK_ICON_VIEW_DROP_LEFT); + } + + if (path) + gtk_tree_path_free (path); + + return TRUE; +} + +static GtkTreePath* +get_logical_destination (GtkIconView *icon_view, + gboolean *drop_append_mode) +{ + /* adjust path to point to the row the drop goes in front of */ + GtkTreePath *path = NULL; + GtkIconViewDropPosition pos; + + *drop_append_mode = FALSE; + + gtk_icon_view_get_drag_dest_item (icon_view, &path, &pos); + + if (path == NULL) + return NULL; + + if (pos == GTK_ICON_VIEW_DROP_RIGHT || + pos == GTK_ICON_VIEW_DROP_BELOW) + { + GtkTreeIter iter; + GtkTreeModel *model = icon_view->priv->model; + + if (!gtk_tree_model_get_iter (model, &iter, path) || + !gtk_tree_model_iter_next (model, &iter)) + *drop_append_mode = TRUE; + else + { + *drop_append_mode = FALSE; + gtk_tree_path_next (path); + } + } + + return path; +} + +static gboolean +gtk_icon_view_maybe_begin_drag (GtkIconView *icon_view, + double x, + double y, + GdkDevice *device) +{ + GtkTreePath *path = NULL; + GtkTreeModel *model; + gboolean retval = FALSE; + GdkContentProvider *content; + GdkPaintable *icon; + GtkIconViewItem *item; + GdkSurface *surface; + GdkDrag *drag; + + if (!icon_view->priv->source_set) + goto out; + + if (icon_view->priv->pressed_button < 0) + goto out; + + if (!gtk_drag_check_threshold_double (GTK_WIDGET (icon_view), + icon_view->priv->press_start_x, + icon_view->priv->press_start_y, + x, y)) + goto out; + + model = gtk_icon_view_get_model (icon_view); + + if (model == NULL) + goto out; + + icon_view->priv->pressed_button = -1; + + item = _gtk_icon_view_get_item_at_coords (icon_view, + icon_view->priv->press_start_x, + icon_view->priv->press_start_y, + TRUE, + NULL); + + if (item == NULL) + goto out; + + path = gtk_tree_path_new_from_indices (item->index, -1); + + if (!GTK_IS_TREE_DRAG_SOURCE (model) || + !gtk_tree_drag_source_row_draggable (GTK_TREE_DRAG_SOURCE (model), path)) + goto out; + + /* FIXME Check whether we're a start button, if not return FALSE and + * free path + */ + + /* Now we can begin the drag */ + + retval = TRUE; + + surface = gtk_native_get_surface (gtk_widget_get_native (GTK_WIDGET (icon_view))); + + content = gtk_icon_view_drag_data_get (icon_view, path); + if (content == NULL) + goto out; + + drag = gdk_drag_begin (surface, + device, + content, + icon_view->priv->source_actions, + icon_view->priv->press_start_x, + icon_view->priv->press_start_y); + + g_object_unref (content); + + g_signal_connect (drag, "dnd-finished", G_CALLBACK (gtk_icon_view_dnd_finished_cb), icon_view); + + icon_view->priv->source_item = gtk_tree_row_reference_new (model, path); + + x = icon_view->priv->press_start_x - item->cell_area.x + icon_view->priv->item_padding; + y = icon_view->priv->press_start_y - item->cell_area.y + icon_view->priv->item_padding; + + icon = gtk_icon_view_create_drag_icon (icon_view, path); + gtk_drag_icon_set_from_paintable (drag, icon, x, y); + g_object_unref (icon); + + icon_view->priv->drag = drag; + + g_object_unref (drag); + + out: + if (path) + gtk_tree_path_free (path); + + return retval; +} + +/* Source side drag signals */ +static GdkContentProvider * +gtk_icon_view_drag_data_get (GtkIconView *icon_view, + GtkTreePath *source_row) +{ + GdkContentProvider *content; + GtkTreeModel *model; + + model = gtk_icon_view_get_model (icon_view); + + if (model == NULL) + return NULL; + + if (!icon_view->priv->source_set) + return NULL; + + /* We can implement the GTK_TREE_MODEL_ROW target generically for + * any model; for DragSource models there are some other formats + * we also support. + */ + + if (GTK_IS_TREE_DRAG_SOURCE (model)) + content = gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (model), source_row); + else + content = NULL; + + /* If drag_data_get does nothing, try providing row data. */ + if (content == NULL) + content = gtk_tree_create_row_drag_content (model, source_row); + + return content; +} + +static void +gtk_icon_view_dnd_finished_cb (GdkDrag *drag, + GtkWidget *widget) +{ + GtkTreeModel *model; + GtkIconView *icon_view; + GtkTreePath *source_row; + + if (gdk_drag_get_selected_action (drag) != GDK_ACTION_MOVE) + return; + + icon_view = GTK_ICON_VIEW (widget); + model = gtk_icon_view_get_model (icon_view); + + if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_SOURCE, "drag-data-delete")) + return; + + if (!icon_view->priv->source_set) + return; + + source_row = gtk_tree_row_reference_get_path (icon_view->priv->source_item); + if (source_row == NULL) + return; + + gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (model), source_row); + + gtk_tree_path_free (source_row); + + g_clear_pointer (&icon_view->priv->source_item, gtk_tree_row_reference_free); + icon_view->priv->drag = NULL; +} + +/* Target side drag signals */ +static void +gtk_icon_view_drag_leave (GtkDropTargetAsync *dest, + GdkDrop *drop, + GtkIconView *icon_view) +{ + /* unset any highlight row */ + gtk_icon_view_set_drag_dest_item (icon_view, + NULL, + GTK_ICON_VIEW_DROP_LEFT); + + remove_scroll_timeout (icon_view); +} + +static GdkDragAction +gtk_icon_view_drag_motion (GtkDropTargetAsync *dest, + GdkDrop *drop, + double x, + double y, + GtkIconView *icon_view) +{ + GtkTreePath *path = NULL; + GtkIconViewDropPosition pos; + GdkDragAction suggested_action = 0; + GType target; + gboolean empty; + GdkDragAction result; + + if (!set_destination (icon_view, drop, dest, x, y, &suggested_action, &target)) + return 0; + + gtk_icon_view_get_drag_dest_item (icon_view, &path, &pos); + + /* we only know this *after* set_desination_row */ + empty = icon_view->priv->empty_view_drop; + + if (path == NULL && !empty) + { + /* Can't drop here. */ + result = 0; + } + else + { + if (icon_view->priv->scroll_timeout_id == 0) + { + icon_view->priv->scroll_timeout_id = g_timeout_add (50, drag_scroll_timeout, icon_view); + gdk_source_set_static_name_by_id (icon_view->priv->scroll_timeout_id, "[gtk] drag_scroll_timeout"); + } + + if (target == GTK_TYPE_TREE_ROW_DATA) + { + /* Request data so we can use the source row when + * determining whether to accept the drop + */ + set_status_pending (drop, suggested_action); + gdk_drop_read_value_async (drop, target, G_PRIORITY_DEFAULT, NULL, gtk_icon_view_drag_data_received, icon_view); + } + else + { + set_status_pending (drop, 0); + } + result = suggested_action; + } + + if (path) + gtk_tree_path_free (path); + + return result; +} + +static gboolean +gtk_icon_view_drag_drop (GtkDropTargetAsync *dest, + GdkDrop *drop, + double x, + double y, + GtkIconView *icon_view) +{ + GtkTreePath *path; + GdkDragAction suggested_action = 0; + GType target = G_TYPE_INVALID; + GtkTreeModel *model; + gboolean drop_append_mode; + + model = gtk_icon_view_get_model (icon_view); + + remove_scroll_timeout (icon_view); + + if (!icon_view->priv->dest_set) + return FALSE; + + if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drop")) + return FALSE; + + if (!set_destination (icon_view, drop, dest, x, y, &suggested_action, &target)) + return FALSE; + + path = get_logical_destination (icon_view, &drop_append_mode); + + if (target != G_TYPE_INVALID && path != NULL) + { + /* in case a motion had requested drag data, change things so we + * treat drag data receives as a drop. + */ + set_status_pending (drop, 0); + set_dest_row (drop, model, path, + icon_view->priv->empty_view_drop, drop_append_mode); + } + + if (path) + gtk_tree_path_free (path); + + /* Unset this thing */ + gtk_icon_view_set_drag_dest_item (icon_view, NULL, GTK_ICON_VIEW_DROP_LEFT); + + if (target != G_TYPE_INVALID) + { + gdk_drop_read_value_async (drop, target, G_PRIORITY_DEFAULT, NULL, gtk_icon_view_drag_data_received, icon_view); + return TRUE; + } + else + return FALSE; +} + +static void +gtk_icon_view_drag_data_received (GObject *source, + GAsyncResult *result, + gpointer data) +{ + GtkIconView *icon_view = data; + GdkDrop *drop = GDK_DROP (source); + GtkTreePath *path; + GtkTreeModel *model; + GtkTreePath *dest_row; + GdkDragAction suggested_action; + gboolean drop_append_mode; + const GValue *value; + + value = gdk_drop_read_value_finish (drop, result, NULL); + if (!value) + return; + + model = gtk_icon_view_get_model (icon_view); + + if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag-data-received")) + return; + + if (!icon_view->priv->dest_set) + return; + + suggested_action = get_status_pending (drop); + + if (suggested_action) + { + /* We are getting this data due to a request in drag_motion, + * rather than due to a request in drag_drop, so we are just + * supposed to call drag_status, not actually paste in the + * data. + */ + path = get_logical_destination (icon_view, &drop_append_mode); + + if (path == NULL) + suggested_action = 0; + + if (suggested_action) + { + if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model), + path, + value)) + suggested_action = 0; + } + + if (path) + gtk_tree_path_free (path); + + /* If you can't drop, remove user drop indicator until the next motion */ + if (suggested_action == 0) + gtk_icon_view_set_drag_dest_item (icon_view, + NULL, + GTK_ICON_VIEW_DROP_LEFT); + return; + } + + + dest_row = get_dest_row (drop); + + if (dest_row == NULL) + return; + + suggested_action = gtk_icon_view_get_action (GTK_WIDGET (icon_view), drop); + + if (suggested_action && + !gtk_tree_drag_dest_drag_data_received (GTK_TREE_DRAG_DEST (model), + dest_row, + value)) + suggested_action = 0; + + gdk_drop_finish (drop, suggested_action); + + gtk_tree_path_free (dest_row); + + /* drop dest_row */ + set_dest_row (drop, NULL, NULL, FALSE, FALSE); +} + +/* Drag-and-Drop support */ + +/** + * gtk_icon_view_enable_model_drag_source: + * @icon_view: a `GtkIconView` + * @start_button_mask: Mask of allowed buttons to start drag + * @formats: the formats that the drag will support + * @actions: the bitmask of possible actions for a drag from this + * widget + * + * Turns @icon_view into a drag source for automatic DND. Calling this + * method sets `GtkIconView`:reorderable to %FALSE. + * + * Deprecated: 4.10: Use GtkGridView instead + **/ +void +gtk_icon_view_enable_model_drag_source (GtkIconView *icon_view, + GdkModifierType start_button_mask, + GdkContentFormats *formats, + GdkDragAction actions) +{ + g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); + + icon_view->priv->source_formats = gdk_content_formats_ref (formats); + icon_view->priv->source_actions = actions; + + icon_view->priv->source_set = TRUE; + + unset_reorderable (icon_view); +} + +/** + * gtk_icon_view_enable_model_drag_dest: + * @icon_view: a `GtkIconView` + * @formats: the formats that the drag will support + * @actions: the bitmask of possible actions for a drag to this + * widget + * + * Turns @icon_view into a drop destination for automatic DND. Calling this + * method sets `GtkIconView`:reorderable to %FALSE. + * + * Deprecated: 4.10: Use GtkGridView instead + **/ +void +gtk_icon_view_enable_model_drag_dest (GtkIconView *icon_view, + GdkContentFormats *formats, + GdkDragAction actions) +{ + GtkCssNode *widget_node; + + g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); + + icon_view->priv->dest = gtk_drop_target_async_new (gdk_content_formats_ref (formats), actions); + g_signal_connect (icon_view->priv->dest, "drag-leave", G_CALLBACK (gtk_icon_view_drag_leave), icon_view); + g_signal_connect (icon_view->priv->dest, "drag-enter", G_CALLBACK (gtk_icon_view_drag_motion), icon_view); + g_signal_connect (icon_view->priv->dest, "drag-motion", G_CALLBACK (gtk_icon_view_drag_motion), icon_view); + g_signal_connect (icon_view->priv->dest, "drop", G_CALLBACK (gtk_icon_view_drag_drop), icon_view); + gtk_widget_add_controller (GTK_WIDGET (icon_view), GTK_EVENT_CONTROLLER (icon_view->priv->dest)); + + icon_view->priv->dest_actions = actions; + + icon_view->priv->dest_set = TRUE; + + unset_reorderable (icon_view); + + widget_node = gtk_widget_get_css_node (GTK_WIDGET (icon_view)); + icon_view->priv->dndnode = gtk_css_node_new (); + gtk_css_node_set_name (icon_view->priv->dndnode, g_quark_from_static_string ("dndtarget")); + gtk_css_node_set_parent (icon_view->priv->dndnode, widget_node); + gtk_css_node_set_state (icon_view->priv->dndnode, gtk_css_node_get_state (widget_node)); + g_object_unref (icon_view->priv->dndnode); +} + +/** + * gtk_icon_view_unset_model_drag_source: + * @icon_view: a `GtkIconView` + * + * Undoes the effect of gtk_icon_view_enable_model_drag_source(). Calling this + * method sets `GtkIconView`:reorderable to %FALSE. + * + * Deprecated: 4.10: Use GtkGridView instead + **/ +void +gtk_icon_view_unset_model_drag_source (GtkIconView *icon_view) +{ + g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); + + if (icon_view->priv->source_set) + { + g_clear_pointer (&icon_view->priv->source_formats, gdk_content_formats_unref); + icon_view->priv->source_set = FALSE; + } + + unset_reorderable (icon_view); +} + +/** + * gtk_icon_view_unset_model_drag_dest: + * @icon_view: a `GtkIconView` + * + * Undoes the effect of gtk_icon_view_enable_model_drag_dest(). Calling this + * method sets `GtkIconView`:reorderable to %FALSE. + * + * Deprecated: 4.10: Use GtkGridView instead + **/ +void +gtk_icon_view_unset_model_drag_dest (GtkIconView *icon_view) +{ + g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); + + if (icon_view->priv->dest_set) + { + gtk_widget_remove_controller (GTK_WIDGET (icon_view), GTK_EVENT_CONTROLLER (icon_view->priv->dest)); + icon_view->priv->dest = NULL; + icon_view->priv->dest_set = FALSE; + + gtk_css_node_set_parent (icon_view->priv->dndnode, NULL); + icon_view->priv->dndnode = NULL; + } + + unset_reorderable (icon_view); +} + +/* These are useful to implement your own custom stuff. */ +/** + * gtk_icon_view_set_drag_dest_item: + * @icon_view: a `GtkIconView` + * @path: (nullable): The path of the item to highlight + * @pos: Specifies where to drop, relative to the item + * + * Sets the item that is highlighted for feedback. + * + * Deprecated: 4.10: Use GtkGridView instead + */ +void +gtk_icon_view_set_drag_dest_item (GtkIconView *icon_view, + GtkTreePath *path, + GtkIconViewDropPosition pos) +{ + /* Note; this function is exported to allow a custom DND + * implementation, so it can't touch TreeViewDragInfo + */ + + g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); + + if (icon_view->priv->dest_item) + { + GtkTreePath *current_path; + current_path = gtk_tree_row_reference_get_path (icon_view->priv->dest_item); + gtk_tree_row_reference_free (icon_view->priv->dest_item); + icon_view->priv->dest_item = NULL; + + gtk_icon_view_queue_draw_path (icon_view, current_path); + gtk_tree_path_free (current_path); + } + + /* special case a drop on an empty model */ + icon_view->priv->empty_view_drop = FALSE; + if (pos == GTK_ICON_VIEW_DROP_ABOVE && path + && gtk_tree_path_get_depth (path) == 1 + && gtk_tree_path_get_indices (path)[0] == 0) + { + int n_children; + + n_children = gtk_tree_model_iter_n_children (icon_view->priv->model, + NULL); + + if (n_children == 0) + icon_view->priv->empty_view_drop = TRUE; + } + + icon_view->priv->dest_pos = pos; + + if (path) + { + icon_view->priv->dest_item = + gtk_tree_row_reference_new_proxy (G_OBJECT (icon_view), + icon_view->priv->model, path); + + gtk_icon_view_queue_draw_path (icon_view, path); + } +} + +/** + * gtk_icon_view_get_drag_dest_item: + * @icon_view: a `GtkIconView` + * @path: (out) (nullable) (optional): Return location for the path of + * the highlighted item + * @pos: (out) (optional): Return location for the drop position + * + * Gets information about the item that is highlighted for feedback. + * + * Deprecated: 4.10: Use GtkGridView instead + */ +void +gtk_icon_view_get_drag_dest_item (GtkIconView *icon_view, + GtkTreePath **path, + GtkIconViewDropPosition *pos) +{ + g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); + + if (path) + { + if (icon_view->priv->dest_item) + *path = gtk_tree_row_reference_get_path (icon_view->priv->dest_item); + else + *path = NULL; + } + + if (pos) + *pos = icon_view->priv->dest_pos; +} + +/** + * gtk_icon_view_get_dest_item_at_pos: + * @icon_view: a `GtkIconView` + * @drag_x: the position to determine the destination item for + * @drag_y: the position to determine the destination item for + * @path: (out) (optional): Return location for the path of the item + * @pos: (out) (optional): Return location for the drop position + * + * Determines the destination item for a given position. + * + * Returns: whether there is an item at the given position. + * + * Deprecated: 4.10: Use GtkGridView instead + **/ +gboolean +gtk_icon_view_get_dest_item_at_pos (GtkIconView *icon_view, + int drag_x, + int drag_y, + GtkTreePath **path, + GtkIconViewDropPosition *pos) +{ + GtkIconViewItem *item; + + /* Note; this function is exported to allow a custom DND + * implementation, so it can't touch TreeViewDragInfo + */ + + g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), FALSE); + g_return_val_if_fail (drag_x >= 0, FALSE); + g_return_val_if_fail (drag_y >= 0, FALSE); + + + if (path) + *path = NULL; + + item = _gtk_icon_view_get_item_at_coords (icon_view, + drag_x + gtk_adjustment_get_value (icon_view->priv->hadjustment), + drag_y + gtk_adjustment_get_value (icon_view->priv->vadjustment), + FALSE, NULL); + + if (item == NULL) + return FALSE; + + if (path) + *path = gtk_tree_path_new_from_indices (item->index, -1); + + if (pos) + { + if (drag_x < item->cell_area.x + item->cell_area.width / 4) + *pos = GTK_ICON_VIEW_DROP_LEFT; + else if (drag_x > item->cell_area.x + item->cell_area.width * 3 / 4) + *pos = GTK_ICON_VIEW_DROP_RIGHT; + else if (drag_y < item->cell_area.y + item->cell_area.height / 4) + *pos = GTK_ICON_VIEW_DROP_ABOVE; + else if (drag_y > item->cell_area.y + item->cell_area.height * 3 / 4) + *pos = GTK_ICON_VIEW_DROP_BELOW; + else + *pos = GTK_ICON_VIEW_DROP_INTO; + } + + return TRUE; +} + +/** + * gtk_icon_view_create_drag_icon: + * @icon_view: a `GtkIconView` + * @path: a `GtkTreePath` in @icon_view + * + * Creates a `GdkPaintable` representation of the item at @path. + * This image is used for a drag icon. + * + * Returns: (transfer full) (nullable): a newly-allocated `GdkPaintable` of the drag icon. + * + * Deprecated: 4.10: Use GtkGridView instead + **/ +GdkPaintable * +gtk_icon_view_create_drag_icon (GtkIconView *icon_view, + GtkTreePath *path) +{ + GtkWidget *widget; + GtkSnapshot *snapshot; + GdkPaintable *paintable; + GList *l; + int index; + + g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), NULL); + g_return_val_if_fail (path != NULL, NULL); + + widget = GTK_WIDGET (icon_view); + + if (!gtk_widget_get_realized (widget)) + return NULL; + + index = gtk_tree_path_get_indices (path)[0]; + + for (l = icon_view->priv->items; l; l = l->next) + { + GtkIconViewItem *item = l->data; + + if (index == item->index) + { + snapshot = gtk_snapshot_new (); + gtk_icon_view_snapshot_item (icon_view, snapshot, item, + icon_view->priv->item_padding, + icon_view->priv->item_padding, + FALSE); + paintable = gtk_snapshot_free_to_paintable (snapshot, NULL); + + return paintable; + } + } + + return NULL; +} + +/** + * gtk_icon_view_get_reorderable: + * @icon_view: a `GtkIconView` + * + * Retrieves whether the user can reorder the list via drag-and-drop. + * See gtk_icon_view_set_reorderable(). + * + * Returns: %TRUE if the list can be reordered. + * + * Deprecated: 4.10: Use GtkGridView instead + **/ +gboolean +gtk_icon_view_get_reorderable (GtkIconView *icon_view) +{ + g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), FALSE); + + return icon_view->priv->reorderable; +} + +/** + * gtk_icon_view_set_reorderable: + * @icon_view: A `GtkIconView`. + * @reorderable: %TRUE, if the list of items can be reordered. + * + * This function is a convenience function to allow you to reorder models that + * support the `GtkTreeDragSourceIface` and the `GtkTreeDragDestIface`. Both + * `GtkTreeStore` and `GtkListStore` support these. If @reorderable is %TRUE, then + * the user can reorder the model by dragging and dropping rows. The + * developer can listen to these changes by connecting to the model's + * row_inserted and row_deleted signals. The reordering is implemented by setting up + * the icon view as a drag source and destination. Therefore, drag and + * drop can not be used in a reorderable view for any other purpose. + * + * This function does not give you any degree of control over the order -- any + * reordering is allowed. If more control is needed, you should probably + * handle drag and drop manually. + * + * Deprecated: 4.10: Use GtkGridView instead + **/ +void +gtk_icon_view_set_reorderable (GtkIconView *icon_view, + gboolean reorderable) +{ + g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); + + reorderable = reorderable != FALSE; + + if (icon_view->priv->reorderable == reorderable) + return; + + if (reorderable) + { + GdkContentFormats *formats = gdk_content_formats_new_for_gtype (GTK_TYPE_TREE_ROW_DATA); + gtk_icon_view_enable_model_drag_source (icon_view, + GDK_BUTTON1_MASK, + formats, + GDK_ACTION_MOVE); + gtk_icon_view_enable_model_drag_dest (icon_view, + formats, + GDK_ACTION_MOVE); + gdk_content_formats_unref (formats); + } + else + { + gtk_icon_view_unset_model_drag_source (icon_view); + gtk_icon_view_unset_model_drag_dest (icon_view); + } + + icon_view->priv->reorderable = reorderable; + + g_object_notify (G_OBJECT (icon_view), "reorderable"); +} + +/** + * gtk_icon_view_set_activate_on_single_click: + * @icon_view: a `GtkIconView` + * @single: %TRUE to emit item-activated on a single click + * + * Causes the `GtkIconView`::item-activated signal to be emitted on + * a single click instead of a double click. + * + * Deprecated: 4.10: Use GtkGridView instead + **/ +void +gtk_icon_view_set_activate_on_single_click (GtkIconView *icon_view, + gboolean single) +{ + g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); + + single = single != FALSE; + + if (icon_view->priv->activate_on_single_click == single) + return; + + icon_view->priv->activate_on_single_click = single; + g_object_notify (G_OBJECT (icon_view), "activate-on-single-click"); +} + +/** + * gtk_icon_view_get_activate_on_single_click: + * @icon_view: a `GtkIconView` + * + * Gets the setting set by gtk_icon_view_set_activate_on_single_click(). + * + * Returns: %TRUE if item-activated will be emitted on a single click + * + * Deprecated: 4.10: Use GtkGridView instead + **/ +gboolean +gtk_icon_view_get_activate_on_single_click (GtkIconView *icon_view) +{ + g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), FALSE); + + return icon_view->priv->activate_on_single_click; +} + +static gboolean +gtk_icon_view_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const char *tagname, + GtkBuildableParser *parser, + gpointer *data) +{ + if (parent_buildable_iface->custom_tag_start (buildable, builder, child, + tagname, parser, data)) + return TRUE; + + return _gtk_cell_layout_buildable_custom_tag_start (buildable, builder, child, + tagname, parser, data); +} + +static void +gtk_icon_view_buildable_custom_tag_end (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const char *tagname, + gpointer data) +{ + if (!_gtk_cell_layout_buildable_custom_tag_end (buildable, builder, + child, tagname, data)) + parent_buildable_iface->custom_tag_end (buildable, builder, + child, tagname, data); +} diff --git a/gtk/deprecated/gtkiconview.h b/gtk/deprecated/gtkiconview.h new file mode 100644 index 0000000000..dc98100bd0 --- /dev/null +++ b/gtk/deprecated/gtkiconview.h @@ -0,0 +1,289 @@ +/* gtkiconview.h + * Copyright (C) 2002, 2004 Anders Carlsson + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#ifndef __GTK_ICON_VIEW_H__ +#define __GTK_ICON_VIEW_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include +#include +#include +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_ICON_VIEW (gtk_icon_view_get_type ()) +#define GTK_ICON_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_ICON_VIEW, GtkIconView)) +#define GTK_IS_ICON_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_ICON_VIEW)) + +typedef struct _GtkIconView GtkIconView; + +/** + * GtkIconViewForeachFunc: + * @icon_view: a `GtkIconView` + * @path: The `GtkTreePath` of a selected row + * @data: (closure): user data + * + * A function used by gtk_icon_view_selected_foreach() to map all + * selected rows. + * + * It will be called on every selected row in the view. + */ +typedef void (* GtkIconViewForeachFunc) (GtkIconView *icon_view, + GtkTreePath *path, + gpointer data); + +/** + * GtkIconViewDropPosition: + * @GTK_ICON_VIEW_NO_DROP: no drop possible + * @GTK_ICON_VIEW_DROP_INTO: dropped item replaces the item + * @GTK_ICON_VIEW_DROP_LEFT: dropped item is inserted to the left + * @GTK_ICON_VIEW_DROP_RIGHT: dropped item is inserted to the right + * @GTK_ICON_VIEW_DROP_ABOVE: dropped item is inserted above + * @GTK_ICON_VIEW_DROP_BELOW: dropped item is inserted below + * + * An enum for determining where a dropped item goes. + */ +typedef enum +{ + GTK_ICON_VIEW_NO_DROP, + GTK_ICON_VIEW_DROP_INTO, + GTK_ICON_VIEW_DROP_LEFT, + GTK_ICON_VIEW_DROP_RIGHT, + GTK_ICON_VIEW_DROP_ABOVE, + GTK_ICON_VIEW_DROP_BELOW +} GtkIconViewDropPosition; + +GDK_AVAILABLE_IN_ALL +GType gtk_icon_view_get_type (void) G_GNUC_CONST; +GDK_DEPRECATED_IN_4_10 +GtkWidget * gtk_icon_view_new (void); +GDK_DEPRECATED_IN_4_10 +GtkWidget * gtk_icon_view_new_with_area (GtkCellArea *area); +GDK_DEPRECATED_IN_4_10 +GtkWidget * gtk_icon_view_new_with_model (GtkTreeModel *model); + +GDK_DEPRECATED_IN_4_10 +void gtk_icon_view_set_model (GtkIconView *icon_view, + GtkTreeModel *model); +GDK_DEPRECATED_IN_4_10 +GtkTreeModel * gtk_icon_view_get_model (GtkIconView *icon_view); +GDK_DEPRECATED_IN_4_10 +void gtk_icon_view_set_text_column (GtkIconView *icon_view, + int column); +GDK_DEPRECATED_IN_4_10 +int gtk_icon_view_get_text_column (GtkIconView *icon_view); +GDK_DEPRECATED_IN_4_10 +void gtk_icon_view_set_markup_column (GtkIconView *icon_view, + int column); +GDK_DEPRECATED_IN_4_10 +int gtk_icon_view_get_markup_column (GtkIconView *icon_view); +GDK_DEPRECATED_IN_4_10 +void gtk_icon_view_set_pixbuf_column (GtkIconView *icon_view, + int column); +GDK_DEPRECATED_IN_4_10 +int gtk_icon_view_get_pixbuf_column (GtkIconView *icon_view); + +GDK_DEPRECATED_IN_4_10 +void gtk_icon_view_set_item_orientation (GtkIconView *icon_view, + GtkOrientation orientation); +GDK_DEPRECATED_IN_4_10 +GtkOrientation gtk_icon_view_get_item_orientation (GtkIconView *icon_view); +GDK_DEPRECATED_IN_4_10 +void gtk_icon_view_set_columns (GtkIconView *icon_view, + int columns); +GDK_DEPRECATED_IN_4_10 +int gtk_icon_view_get_columns (GtkIconView *icon_view); +GDK_DEPRECATED_IN_4_10 +void gtk_icon_view_set_item_width (GtkIconView *icon_view, + int item_width); +GDK_DEPRECATED_IN_4_10 +int gtk_icon_view_get_item_width (GtkIconView *icon_view); +GDK_DEPRECATED_IN_4_10 +void gtk_icon_view_set_spacing (GtkIconView *icon_view, + int spacing); +GDK_DEPRECATED_IN_4_10 +int gtk_icon_view_get_spacing (GtkIconView *icon_view); +GDK_DEPRECATED_IN_4_10 +void gtk_icon_view_set_row_spacing (GtkIconView *icon_view, + int row_spacing); +GDK_DEPRECATED_IN_4_10 +int gtk_icon_view_get_row_spacing (GtkIconView *icon_view); +GDK_DEPRECATED_IN_4_10 +void gtk_icon_view_set_column_spacing (GtkIconView *icon_view, + int column_spacing); +GDK_DEPRECATED_IN_4_10 +int gtk_icon_view_get_column_spacing (GtkIconView *icon_view); +GDK_DEPRECATED_IN_4_10 +void gtk_icon_view_set_margin (GtkIconView *icon_view, + int margin); +GDK_DEPRECATED_IN_4_10 +int gtk_icon_view_get_margin (GtkIconView *icon_view); +GDK_DEPRECATED_IN_4_10 +void gtk_icon_view_set_item_padding (GtkIconView *icon_view, + int item_padding); +GDK_DEPRECATED_IN_4_10 +int gtk_icon_view_get_item_padding (GtkIconView *icon_view); + +GDK_DEPRECATED_IN_4_10 +GtkTreePath * gtk_icon_view_get_path_at_pos (GtkIconView *icon_view, + int x, + int y); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_icon_view_get_item_at_pos (GtkIconView *icon_view, + int x, + int y, + GtkTreePath **path, + GtkCellRenderer **cell); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_icon_view_get_visible_range (GtkIconView *icon_view, + GtkTreePath **start_path, + GtkTreePath **end_path); +GDK_DEPRECATED_IN_4_10 +void gtk_icon_view_set_activate_on_single_click (GtkIconView *icon_view, + gboolean single); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_icon_view_get_activate_on_single_click (GtkIconView *icon_view); + +GDK_DEPRECATED_IN_4_10 +void gtk_icon_view_selected_foreach (GtkIconView *icon_view, + GtkIconViewForeachFunc func, + gpointer data); +GDK_DEPRECATED_IN_4_10 +void gtk_icon_view_set_selection_mode (GtkIconView *icon_view, + GtkSelectionMode mode); +GDK_DEPRECATED_IN_4_10 +GtkSelectionMode gtk_icon_view_get_selection_mode (GtkIconView *icon_view); +GDK_DEPRECATED_IN_4_10 +void gtk_icon_view_select_path (GtkIconView *icon_view, + GtkTreePath *path); +GDK_DEPRECATED_IN_4_10 +void gtk_icon_view_unselect_path (GtkIconView *icon_view, + GtkTreePath *path); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_icon_view_path_is_selected (GtkIconView *icon_view, + GtkTreePath *path); +GDK_DEPRECATED_IN_4_10 +int gtk_icon_view_get_item_row (GtkIconView *icon_view, + GtkTreePath *path); +GDK_DEPRECATED_IN_4_10 +int gtk_icon_view_get_item_column (GtkIconView *icon_view, + GtkTreePath *path); +GDK_DEPRECATED_IN_4_10 +GList *gtk_icon_view_get_selected_items (GtkIconView *icon_view); +GDK_DEPRECATED_IN_4_10 +void gtk_icon_view_select_all (GtkIconView *icon_view); +GDK_DEPRECATED_IN_4_10 +void gtk_icon_view_unselect_all (GtkIconView *icon_view); +GDK_DEPRECATED_IN_4_10 +void gtk_icon_view_item_activated (GtkIconView *icon_view, + GtkTreePath *path); +GDK_DEPRECATED_IN_4_10 +void gtk_icon_view_set_cursor (GtkIconView *icon_view, + GtkTreePath *path, + GtkCellRenderer *cell, + gboolean start_editing); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_icon_view_get_cursor (GtkIconView *icon_view, + GtkTreePath **path, + GtkCellRenderer **cell); +GDK_DEPRECATED_IN_4_10 +void gtk_icon_view_scroll_to_path (GtkIconView *icon_view, + GtkTreePath *path, + gboolean use_align, + float row_align, + float col_align); + +/* Drag-and-Drop support */ +GDK_DEPRECATED_IN_4_10 +void gtk_icon_view_enable_model_drag_source (GtkIconView *icon_view, + GdkModifierType start_button_mask, + GdkContentFormats *formats, + GdkDragAction actions); +GDK_DEPRECATED_IN_4_10 +void gtk_icon_view_enable_model_drag_dest (GtkIconView *icon_view, + GdkContentFormats *formats, + GdkDragAction actions); +GDK_DEPRECATED_IN_4_10 +void gtk_icon_view_unset_model_drag_source (GtkIconView *icon_view); +GDK_DEPRECATED_IN_4_10 +void gtk_icon_view_unset_model_drag_dest (GtkIconView *icon_view); +GDK_DEPRECATED_IN_4_10 +void gtk_icon_view_set_reorderable (GtkIconView *icon_view, + gboolean reorderable); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_icon_view_get_reorderable (GtkIconView *icon_view); + + +/* These are useful to implement your own custom stuff. */ +GDK_DEPRECATED_IN_4_10 +void gtk_icon_view_set_drag_dest_item (GtkIconView *icon_view, + GtkTreePath *path, + GtkIconViewDropPosition pos); +GDK_DEPRECATED_IN_4_10 +void gtk_icon_view_get_drag_dest_item (GtkIconView *icon_view, + GtkTreePath **path, + GtkIconViewDropPosition *pos); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_icon_view_get_dest_item_at_pos (GtkIconView *icon_view, + int drag_x, + int drag_y, + GtkTreePath **path, + GtkIconViewDropPosition *pos); +GDK_DEPRECATED_IN_4_10 +GdkPaintable *gtk_icon_view_create_drag_icon (GtkIconView *icon_view, + GtkTreePath *path); + +GDK_DEPRECATED_IN_4_10 +gboolean gtk_icon_view_get_cell_rect (GtkIconView *icon_view, + GtkTreePath *path, + GtkCellRenderer *cell, + GdkRectangle *rect); + + +GDK_DEPRECATED_IN_4_10 +void gtk_icon_view_set_tooltip_item (GtkIconView *icon_view, + GtkTooltip *tooltip, + GtkTreePath *path); +GDK_DEPRECATED_IN_4_10 +void gtk_icon_view_set_tooltip_cell (GtkIconView *icon_view, + GtkTooltip *tooltip, + GtkTreePath *path, + GtkCellRenderer *cell); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_icon_view_get_tooltip_context (GtkIconView *icon_view, + int x, + int y, + gboolean keyboard_tip, + GtkTreeModel **model, + GtkTreePath **path, + GtkTreeIter *iter); +GDK_DEPRECATED_IN_4_10 +void gtk_icon_view_set_tooltip_column (GtkIconView *icon_view, + int column); +GDK_DEPRECATED_IN_4_10 +int gtk_icon_view_get_tooltip_column (GtkIconView *icon_view); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkIconView, g_object_unref) + +G_END_DECLS + +#endif /* __GTK_ICON_VIEW_H__ */ diff --git a/gtk/deprecated/gtkiconviewprivate.h b/gtk/deprecated/gtkiconviewprivate.h new file mode 100644 index 0000000000..657366b2b8 --- /dev/null +++ b/gtk/deprecated/gtkiconviewprivate.h @@ -0,0 +1,195 @@ +/* gtkiconview.h + * Copyright (C) 2002, 2004 Anders Carlsson + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#include "gtk/deprecated/gtkiconview.h" +#include "gtk/gtkcssnodeprivate.h" +#include "gtk/gtkeventcontrollermotion.h" +#include "gtk/gtkdragsource.h" +#include "gtk/gtkdroptargetasync.h" +#include "gtk/gtkgestureclick.h" + +#ifndef __GTK_ICON_VIEW_PRIVATE_H__ +#define __GTK_ICON_VIEW_PRIVATE_H__ + +typedef struct _GtkIconViewItem GtkIconViewItem; +struct _GtkIconViewItem +{ + GdkRectangle cell_area; + + int index; + + int row, col; + + guint selected : 1; + guint selected_before_rubberbanding : 1; + +}; + +typedef struct _GtkIconViewClass GtkIconViewClass; +typedef struct _GtkIconViewPrivate GtkIconViewPrivate; + +struct _GtkIconView +{ + GtkWidget parent; + + GtkIconViewPrivate *priv; +}; + +struct _GtkIconViewClass +{ + GtkWidgetClass parent_class; + + void (* item_activated) (GtkIconView *icon_view, + GtkTreePath *path); + void (* selection_changed) (GtkIconView *icon_view); + + void (* select_all) (GtkIconView *icon_view); + void (* unselect_all) (GtkIconView *icon_view); + void (* select_cursor_item) (GtkIconView *icon_view); + void (* toggle_cursor_item) (GtkIconView *icon_view); + gboolean (* move_cursor) (GtkIconView *icon_view, + GtkMovementStep step, + int count, + gboolean extend, + gboolean modify); + gboolean (* activate_cursor_item) (GtkIconView *icon_view); +}; + +struct _GtkIconViewPrivate +{ + GtkCellArea *cell_area; + GtkCellAreaContext *cell_area_context; + + gulong add_editable_id; + gulong remove_editable_id; + gulong context_changed_id; + + GPtrArray *row_contexts; + + int width, height; + double mouse_x; + double mouse_y; + + GtkSelectionMode selection_mode; + + GList *children; + + GtkTreeModel *model; + + GList *items; + + GtkEventController *key_controller; + + GtkAdjustment *hadjustment; + GtkAdjustment *vadjustment; + + int rubberband_x1, rubberband_y1; + int rubberband_x2, rubberband_y2; + GdkDevice *rubberband_device; + GtkCssNode *rubberband_node; + + guint scroll_timeout_id; + int scroll_value_diff; + int event_last_x, event_last_y; + + GtkIconViewItem *anchor_item; + GtkIconViewItem *cursor_item; + + GtkIconViewItem *last_single_clicked; + GtkIconViewItem *last_prelight; + + GtkOrientation item_orientation; + + int columns; + int item_width; + int spacing; + int row_spacing; + int column_spacing; + int margin; + int item_padding; + + int text_column; + int markup_column; + int pixbuf_column; + int tooltip_column; + + GtkCellRenderer *pixbuf_cell; + GtkCellRenderer *text_cell; + + /* Drag-and-drop. */ + GdkModifierType start_button_mask; + int pressed_button; + double press_start_x; + double press_start_y; + + GdkContentFormats *source_formats; + GtkDropTargetAsync *dest; + GtkCssNode *dndnode; + + GdkDrag *drag; + + GdkDragAction source_actions; + GdkDragAction dest_actions; + + GtkTreeRowReference *source_item; + GtkTreeRowReference *dest_item; + GtkIconViewDropPosition dest_pos; + + /* scroll to */ + GtkTreeRowReference *scroll_to_path; + float scroll_to_row_align; + float scroll_to_col_align; + guint scroll_to_use_align : 1; + + guint source_set : 1; + guint dest_set : 1; + guint reorderable : 1; + guint empty_view_drop :1; + guint activate_on_single_click : 1; + + guint modify_selection_pressed : 1; + guint extend_selection_pressed : 1; + + guint draw_focus : 1; + + /* GtkScrollablePolicy needs to be checked when + * driving the scrollable adjustment values */ + guint hscroll_policy : 1; + guint vscroll_policy : 1; + + guint doing_rubberband : 1; + +}; + +void _gtk_icon_view_set_cell_data (GtkIconView *icon_view, + GtkIconViewItem *item); +void _gtk_icon_view_set_cursor_item (GtkIconView *icon_view, + GtkIconViewItem *item, + GtkCellRenderer *cursor_cell); +GtkIconViewItem * _gtk_icon_view_get_item_at_coords (GtkIconView *icon_view, + int x, + int y, + gboolean only_in_cell, + GtkCellRenderer **cell_at_pos); +void _gtk_icon_view_select_item (GtkIconView *icon_view, + GtkIconViewItem *item); +void _gtk_icon_view_unselect_item (GtkIconView *icon_view, + GtkIconViewItem *item); + +G_END_DECLS + +#endif /* __GTK_ICON_VIEW_PRIVATE_H__ */ diff --git a/gtk/deprecated/gtkliststore.c b/gtk/deprecated/gtkliststore.c new file mode 100644 index 0000000000..f87658001e --- /dev/null +++ b/gtk/deprecated/gtkliststore.c @@ -0,0 +1,2706 @@ +/* gtkliststore.c + * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#include "config.h" +#include +#include +#include +#include +#include "gtktreemodel.h" +#include "gtkliststore.h" +#include "gtktreedatalistprivate.h" +#include "gtktreednd.h" +#include "gtkbuildable.h" +#include "gtkbuilderprivate.h" + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + +/** + * GtkListStore: + * + * A list-like data structure that can be used with the [class@Gtk.TreeView]. + * + * The `GtkListStore` object is a list model for use with a `GtkTreeView` + * widget. It implements the `GtkTreeModel` interface, and consequentialy, + * can use all of the methods available there. It also implements the + * `GtkTreeSortable` interface so it can be sorted by the view. + * Finally, it also implements the tree + * [drag](iface.TreeDragSource.html) and [drop](iface.TreeDragDest.html) + * interfaces. + * + * The `GtkListStore` can accept most `GType`s as a column type, though + * it can’t accept all custom types. Internally, it will keep a copy of + * data passed in (such as a string or a boxed pointer). Columns that + * accept `GObject`s are handled a little differently. The + * `GtkListStore` will keep a reference to the object instead of copying the + * value. As a result, if the object is modified, it is up to the + * application writer to call [method@Gtk.TreeModel.row_changed] to emit the + * [signal@Gtk.TreeModel::row_changed] signal. This most commonly affects lists + * with [class@Gdk.Texture]s stored. + * + * An example for creating a simple list store: + * + * ```c + * enum { + * COLUMN_STRING, + * COLUMN_INT, + * COLUMN_BOOLEAN, + * N_COLUMNS + * }; + * + * { + * GtkListStore *list_store; + * GtkTreePath *path; + * GtkTreeIter iter; + * int i; + * + * list_store = gtk_list_store_new (N_COLUMNS, + * G_TYPE_STRING, + * G_TYPE_INT, + * G_TYPE_BOOLEAN); + * + * for (i = 0; i < 10; i++) + * { + * char *some_data; + * + * some_data = get_some_data (i); + * + * // Add a new row to the model + * gtk_list_store_append (list_store, &iter); + * gtk_list_store_set (list_store, &iter, + * COLUMN_STRING, some_data, + * COLUMN_INT, i, + * COLUMN_BOOLEAN, FALSE, + * -1); + * + * // As the store will keep a copy of the string internally, + * // we free some_data. + * g_free (some_data); + * } + * + * // Modify a particular row + * path = gtk_tree_path_new_from_string ("4"); + * gtk_tree_model_get_iter (GTK_TREE_MODEL (list_store), + * &iter, + * path); + * gtk_tree_path_free (path); + * gtk_list_store_set (list_store, &iter, + * COLUMN_BOOLEAN, TRUE, + * -1); + * } + * ``` + * + * # Performance Considerations + * + * Internally, the `GtkListStore` was originally implemented with a linked list + * with a tail pointer. As a result, it was fast at data insertion and deletion, + * and not fast at random data access. The `GtkListStore` sets the + * `GTK_TREE_MODEL_ITERS_PERSIST` flag, which means that `GtkTreeIter`s can be + * cached while the row exists. Thus, if access to a particular row is needed + * often and your code is expected to run on older versions of GTK, it is worth + * keeping the iter around. + * + * # Atomic Operations + * + * It is important to note that only the methods + * gtk_list_store_insert_with_values() and gtk_list_store_insert_with_valuesv() + * are atomic, in the sense that the row is being appended to the store and the + * values filled in in a single operation with regard to `GtkTreeModel` signaling. + * In contrast, using e.g. gtk_list_store_append() and then gtk_list_store_set() + * will first create a row, which triggers the `GtkTreeModel::row-inserted` signal + * on `GtkListStore`. The row, however, is still empty, and any signal handler + * connecting to `GtkTreeModel::row-inserted` on this particular store should be prepared + * for the situation that the row might be empty. This is especially important + * if you are wrapping the `GtkListStore` inside a `GtkTreeModel`Filter and are + * using a `GtkTreeModel`FilterVisibleFunc. Using any of the non-atomic operations + * to append rows to the `GtkListStore` will cause the + * `GtkTreeModel`FilterVisibleFunc to be visited with an empty row first; the + * function must be prepared for that. + * + * # GtkListStore as GtkBuildable + * + * The GtkListStore implementation of the [iface@Gtk.Buildable] interface allows + * to specify the model columns with a `` element that may contain + * multiple `` elements, each specifying one model column. The “type” + * attribute specifies the data type for the column. + * + * Additionally, it is possible to specify content for the list store + * in the UI definition, with the `` element. It can contain multiple + * `` elements, each specifying to content for one row of the list model. + * Inside a ``, the `` elements specify the content for individual cells. + * + * Note that it is probably more common to define your models in the code, + * and one might consider it a layering violation to specify the content of + * a list store in a UI definition, data, not presentation, and common wisdom + * is to separate the two, as far as possible. + * + * An example of a UI Definition fragment for a list store: + * + * ```xml + * + * + * + * + * + * + * + * + * John + * Doe + * 25 + * + * + * Johan + * Dahlin + * 50 + * + * + * + * ``` + */ + + +struct _GtkListStorePrivate +{ + GtkTreeIterCompareFunc default_sort_func; + + GDestroyNotify default_sort_destroy; + GList *sort_list; + GType *column_headers; + + int stamp; + int n_columns; + int sort_column_id; + int length; + + GtkSortType order; + + guint columns_dirty : 1; + + gpointer default_sort_data; + gpointer seq; /* head of the list */ +}; + +#define GTK_LIST_STORE_IS_SORTED(list) (((GtkListStore*)(list))->priv->sort_column_id != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID) +static void gtk_list_store_tree_model_init (GtkTreeModelIface *iface); +static void gtk_list_store_drag_source_init(GtkTreeDragSourceIface *iface); +static void gtk_list_store_drag_dest_init (GtkTreeDragDestIface *iface); +static void gtk_list_store_sortable_init (GtkTreeSortableIface *iface); +static void gtk_list_store_buildable_init (GtkBuildableIface *iface); +static void gtk_list_store_finalize (GObject *object); +static GtkTreeModelFlags gtk_list_store_get_flags (GtkTreeModel *tree_model); +static int gtk_list_store_get_n_columns (GtkTreeModel *tree_model); +static GType gtk_list_store_get_column_type (GtkTreeModel *tree_model, + int index); +static gboolean gtk_list_store_get_iter (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreePath *path); +static GtkTreePath *gtk_list_store_get_path (GtkTreeModel *tree_model, + GtkTreeIter *iter); +static void gtk_list_store_get_value (GtkTreeModel *tree_model, + GtkTreeIter *iter, + int column, + GValue *value); +static gboolean gtk_list_store_iter_next (GtkTreeModel *tree_model, + GtkTreeIter *iter); +static gboolean gtk_list_store_iter_previous (GtkTreeModel *tree_model, + GtkTreeIter *iter); +static gboolean gtk_list_store_iter_children (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent); +static gboolean gtk_list_store_iter_has_child (GtkTreeModel *tree_model, + GtkTreeIter *iter); +static int gtk_list_store_iter_n_children (GtkTreeModel *tree_model, + GtkTreeIter *iter); +static gboolean gtk_list_store_iter_nth_child (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent, + int n); +static gboolean gtk_list_store_iter_parent (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *child); + + +static void gtk_list_store_set_n_columns (GtkListStore *list_store, + int n_columns); +static void gtk_list_store_set_column_type (GtkListStore *list_store, + int column, + GType type); + +static void gtk_list_store_increment_stamp (GtkListStore *list_store); + + +/* Drag and Drop */ +static gboolean real_gtk_list_store_row_draggable (GtkTreeDragSource *drag_source, + GtkTreePath *path); +static gboolean gtk_list_store_drag_data_delete (GtkTreeDragSource *drag_source, + GtkTreePath *path); +static GdkContentProvider * + gtk_list_store_drag_data_get (GtkTreeDragSource *drag_source, + GtkTreePath *path); +static gboolean gtk_list_store_drag_data_received (GtkTreeDragDest *drag_dest, + GtkTreePath *dest, + const GValue *value); +static gboolean gtk_list_store_row_drop_possible (GtkTreeDragDest *drag_dest, + GtkTreePath *dest_path, + const GValue *value); + + +/* sortable */ +static void gtk_list_store_sort (GtkListStore *list_store); +static void gtk_list_store_sort_iter_changed (GtkListStore *list_store, + GtkTreeIter *iter, + int column); +static gboolean gtk_list_store_get_sort_column_id (GtkTreeSortable *sortable, + int *sort_column_id, + GtkSortType *order); +static void gtk_list_store_set_sort_column_id (GtkTreeSortable *sortable, + int sort_column_id, + GtkSortType order); +static void gtk_list_store_set_sort_func (GtkTreeSortable *sortable, + int sort_column_id, + GtkTreeIterCompareFunc func, + gpointer data, + GDestroyNotify destroy); +static void gtk_list_store_set_default_sort_func (GtkTreeSortable *sortable, + GtkTreeIterCompareFunc func, + gpointer data, + GDestroyNotify destroy); +static gboolean gtk_list_store_has_default_sort_func (GtkTreeSortable *sortable); + + +/* buildable */ +static gboolean gtk_list_store_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const char *tagname, + GtkBuildableParser *parser, + gpointer *data); +static void gtk_list_store_buildable_custom_tag_end (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const char *tagname, + gpointer data); + +G_DEFINE_TYPE_WITH_CODE (GtkListStore, gtk_list_store, G_TYPE_OBJECT, + G_ADD_PRIVATE (GtkListStore) + G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, + gtk_list_store_tree_model_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE, + gtk_list_store_drag_source_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_DEST, + gtk_list_store_drag_dest_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_SORTABLE, + gtk_list_store_sortable_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, + gtk_list_store_buildable_init)) + + +static void +gtk_list_store_class_init (GtkListStoreClass *class) +{ + GObjectClass *object_class; + + object_class = (GObjectClass*) class; + + object_class->finalize = gtk_list_store_finalize; +} + +static void +gtk_list_store_tree_model_init (GtkTreeModelIface *iface) +{ + iface->get_flags = gtk_list_store_get_flags; + iface->get_n_columns = gtk_list_store_get_n_columns; + iface->get_column_type = gtk_list_store_get_column_type; + iface->get_iter = gtk_list_store_get_iter; + iface->get_path = gtk_list_store_get_path; + iface->get_value = gtk_list_store_get_value; + iface->iter_next = gtk_list_store_iter_next; + iface->iter_previous = gtk_list_store_iter_previous; + iface->iter_children = gtk_list_store_iter_children; + iface->iter_has_child = gtk_list_store_iter_has_child; + iface->iter_n_children = gtk_list_store_iter_n_children; + iface->iter_nth_child = gtk_list_store_iter_nth_child; + iface->iter_parent = gtk_list_store_iter_parent; +} + +static void +gtk_list_store_drag_source_init (GtkTreeDragSourceIface *iface) +{ + iface->row_draggable = real_gtk_list_store_row_draggable; + iface->drag_data_delete = gtk_list_store_drag_data_delete; + iface->drag_data_get = gtk_list_store_drag_data_get; +} + +static void +gtk_list_store_drag_dest_init (GtkTreeDragDestIface *iface) +{ + iface->drag_data_received = gtk_list_store_drag_data_received; + iface->row_drop_possible = gtk_list_store_row_drop_possible; +} + +static void +gtk_list_store_sortable_init (GtkTreeSortableIface *iface) +{ + iface->get_sort_column_id = gtk_list_store_get_sort_column_id; + iface->set_sort_column_id = gtk_list_store_set_sort_column_id; + iface->set_sort_func = gtk_list_store_set_sort_func; + iface->set_default_sort_func = gtk_list_store_set_default_sort_func; + iface->has_default_sort_func = gtk_list_store_has_default_sort_func; +} + +void +gtk_list_store_buildable_init (GtkBuildableIface *iface) +{ + iface->custom_tag_start = gtk_list_store_buildable_custom_tag_start; + iface->custom_tag_end = gtk_list_store_buildable_custom_tag_end; +} + +static void +gtk_list_store_init (GtkListStore *list_store) +{ + GtkListStorePrivate *priv; + + list_store->priv = gtk_list_store_get_instance_private (list_store); + priv = list_store->priv; + + priv->seq = g_sequence_new (NULL); + priv->sort_list = NULL; + priv->stamp = g_random_int (); + priv->sort_column_id = GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID; + priv->columns_dirty = FALSE; + priv->length = 0; +} + +static gboolean +iter_is_valid (GtkTreeIter *iter, + GtkListStore *list_store) +{ + return iter != NULL && + iter->user_data != NULL && + list_store->priv->stamp == iter->stamp && + !g_sequence_iter_is_end (iter->user_data) && + g_sequence_iter_get_sequence (iter->user_data) == list_store->priv->seq; +} + +/** + * gtk_list_store_new: + * @n_columns: number of columns in the list store + * @...: all `GType` types for the columns, from first to last + * + * Creates a new list store as with @n_columns columns each of the types passed + * in. Note that only types derived from standard GObject fundamental types + * are supported. + * + * As an example, `gtk_list_store_new (3, G_TYPE_INT, G_TYPE_STRING, + * GDK_TYPE_TEXTURE);` will create a new `GtkListStore` with three columns, of type + * int, string and `GdkTexture`, respectively. + * + * Returns: a new `GtkListStore` + * + * Deprecated: 4.10: Use list models + */ +GtkListStore * +gtk_list_store_new (int n_columns, + ...) +{ + GtkListStore *retval; + va_list args; + int i; + + g_return_val_if_fail (n_columns > 0, NULL); + + retval = g_object_new (GTK_TYPE_LIST_STORE, NULL); + gtk_list_store_set_n_columns (retval, n_columns); + + va_start (args, n_columns); + + for (i = 0; i < n_columns; i++) + { + GType type = va_arg (args, GType); + if (! _gtk_tree_data_list_check_type (type)) + { + g_warning ("%s: Invalid type %s", G_STRLOC, g_type_name (type)); + g_object_unref (retval); + va_end (args); + + return NULL; + } + + gtk_list_store_set_column_type (retval, i, type); + } + + va_end (args); + + return retval; +} + + +/** + * gtk_list_store_newv: (rename-to gtk_list_store_new) + * @n_columns: number of columns in the list store + * @types: (array length=n_columns): an array of `GType` types for the columns, from first to last + * + * Non-vararg creation function. Used primarily by language bindings. + * + * Returns: (transfer full): a new `GtkListStore` + * + * Deprecated: 4.10: Use list models + **/ +GtkListStore * +gtk_list_store_newv (int n_columns, + GType *types) +{ + GtkListStore *retval; + int i; + + g_return_val_if_fail (n_columns > 0, NULL); + + retval = g_object_new (GTK_TYPE_LIST_STORE, NULL); + gtk_list_store_set_n_columns (retval, n_columns); + + for (i = 0; i < n_columns; i++) + { + if (! _gtk_tree_data_list_check_type (types[i])) + { + g_warning ("%s: Invalid type %s", G_STRLOC, g_type_name (types[i])); + g_object_unref (retval); + return NULL; + } + + gtk_list_store_set_column_type (retval, i, types[i]); + } + + return retval; +} + +/** + * gtk_list_store_set_column_types: + * @list_store: A `GtkListStore` + * @n_columns: Number of columns for the list store + * @types: (array length=n_columns): An array length n of `GType`s + * + * This function is meant primarily for `GObject`s that inherit from `GtkListStore`, + * and should only be used when constructing a new `GtkListStore`. It will not + * function after a row has been added, or a method on the `GtkTreeModel` + * interface is called. + * + * Deprecated: 4.10: Use list models + **/ +void +gtk_list_store_set_column_types (GtkListStore *list_store, + int n_columns, + GType *types) +{ + GtkListStorePrivate *priv; + int i; + + g_return_if_fail (GTK_IS_LIST_STORE (list_store)); + + priv = list_store->priv; + + g_return_if_fail (priv->columns_dirty == 0); + + gtk_list_store_set_n_columns (list_store, n_columns); + for (i = 0; i < n_columns; i++) + { + if (! _gtk_tree_data_list_check_type (types[i])) + { + g_warning ("%s: Invalid type %s", G_STRLOC, g_type_name (types[i])); + continue; + } + gtk_list_store_set_column_type (list_store, i, types[i]); + } +} + +static void +gtk_list_store_set_n_columns (GtkListStore *list_store, + int n_columns) +{ + GtkListStorePrivate *priv = list_store->priv; + int i; + + if (priv->n_columns == n_columns) + return; + + priv->column_headers = g_renew (GType, priv->column_headers, n_columns); + for (i = priv->n_columns; i < n_columns; i++) + priv->column_headers[i] = G_TYPE_INVALID; + priv->n_columns = n_columns; + + if (priv->sort_list) + _gtk_tree_data_list_header_free (priv->sort_list); + priv->sort_list = _gtk_tree_data_list_header_new (n_columns, priv->column_headers); +} + +static void +gtk_list_store_set_column_type (GtkListStore *list_store, + int column, + GType type) +{ + GtkListStorePrivate *priv = list_store->priv; + + if (!_gtk_tree_data_list_check_type (type)) + { + g_warning ("%s: Invalid type %s", G_STRLOC, g_type_name (type)); + return; + } + + priv->column_headers[column] = type; +} + +static void +gtk_list_store_finalize (GObject *object) +{ + GtkListStore *list_store = GTK_LIST_STORE (object); + GtkListStorePrivate *priv = list_store->priv; + + g_sequence_foreach (priv->seq, + (GFunc) _gtk_tree_data_list_free, priv->column_headers); + + g_sequence_free (priv->seq); + + _gtk_tree_data_list_header_free (priv->sort_list); + g_free (priv->column_headers); + + if (priv->default_sort_destroy) + { + GDestroyNotify d = priv->default_sort_destroy; + + priv->default_sort_destroy = NULL; + d (priv->default_sort_data); + priv->default_sort_data = NULL; + } + + G_OBJECT_CLASS (gtk_list_store_parent_class)->finalize (object); +} + +/* Fulfill the GtkTreeModel requirements */ +static GtkTreeModelFlags +gtk_list_store_get_flags (GtkTreeModel *tree_model) +{ + return GTK_TREE_MODEL_ITERS_PERSIST | GTK_TREE_MODEL_LIST_ONLY; +} + +static int +gtk_list_store_get_n_columns (GtkTreeModel *tree_model) +{ + GtkListStore *list_store = GTK_LIST_STORE (tree_model); + GtkListStorePrivate *priv = list_store->priv; + + priv->columns_dirty = TRUE; + + return priv->n_columns; +} + +static GType +gtk_list_store_get_column_type (GtkTreeModel *tree_model, + int index) +{ + GtkListStore *list_store = GTK_LIST_STORE (tree_model); + GtkListStorePrivate *priv = list_store->priv; + + g_return_val_if_fail (index < priv->n_columns, G_TYPE_INVALID); + + priv->columns_dirty = TRUE; + + return priv->column_headers[index]; +} + +static gboolean +gtk_list_store_get_iter (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreePath *path) +{ + GtkListStore *list_store = GTK_LIST_STORE (tree_model); + GtkListStorePrivate *priv = list_store->priv; + GSequence *seq; + int i; + + priv->columns_dirty = TRUE; + + seq = priv->seq; + + i = gtk_tree_path_get_indices (path)[0]; + + if (i >= g_sequence_get_length (seq)) + { + iter->stamp = 0; + return FALSE; + } + + iter->stamp = priv->stamp; + iter->user_data = g_sequence_get_iter_at_pos (seq, i); + + return TRUE; +} + +static GtkTreePath * +gtk_list_store_get_path (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + GtkListStore *list_store = GTK_LIST_STORE (tree_model); + GtkListStorePrivate *priv = list_store->priv; + GtkTreePath *path; + + g_return_val_if_fail (iter->stamp == priv->stamp, NULL); + + if (g_sequence_iter_is_end (iter->user_data)) + return NULL; + + path = gtk_tree_path_new (); + gtk_tree_path_append_index (path, g_sequence_iter_get_position (iter->user_data)); + + return path; +} + +static void +gtk_list_store_get_value (GtkTreeModel *tree_model, + GtkTreeIter *iter, + int column, + GValue *value) +{ + GtkListStore *list_store = GTK_LIST_STORE (tree_model); + GtkListStorePrivate *priv = list_store->priv; + GtkTreeDataList *list; + int tmp_column = column; + + g_return_if_fail (column < priv->n_columns); + g_return_if_fail (iter_is_valid (iter, list_store)); + + list = g_sequence_get (iter->user_data); + + while (tmp_column-- > 0 && list) + list = list->next; + + if (list == NULL) + g_value_init (value, priv->column_headers[column]); + else + _gtk_tree_data_list_node_to_value (list, + priv->column_headers[column], + value); +} + +static gboolean +gtk_list_store_iter_next (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + GtkListStore *list_store = GTK_LIST_STORE (tree_model); + GtkListStorePrivate *priv = list_store->priv; + gboolean retval; + + g_return_val_if_fail (priv->stamp == iter->stamp, FALSE); + iter->user_data = g_sequence_iter_next (iter->user_data); + + retval = g_sequence_iter_is_end (iter->user_data); + if (retval) + iter->stamp = 0; + + return !retval; +} + +static gboolean +gtk_list_store_iter_previous (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + GtkListStore *list_store = GTK_LIST_STORE (tree_model); + GtkListStorePrivate *priv = list_store->priv; + + g_return_val_if_fail (priv->stamp == iter->stamp, FALSE); + + if (g_sequence_iter_is_begin (iter->user_data)) + { + iter->stamp = 0; + return FALSE; + } + + iter->user_data = g_sequence_iter_prev (iter->user_data); + + return TRUE; +} + +static gboolean +gtk_list_store_iter_children (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent) +{ + GtkListStore *list_store = (GtkListStore *) tree_model; + GtkListStorePrivate *priv = list_store->priv; + + /* this is a list, nodes have no children */ + if (parent) + { + iter->stamp = 0; + return FALSE; + } + + if (g_sequence_get_length (priv->seq) > 0) + { + iter->stamp = priv->stamp; + iter->user_data = g_sequence_get_begin_iter (priv->seq); + return TRUE; + } + else + { + iter->stamp = 0; + return FALSE; + } +} + +static gboolean +gtk_list_store_iter_has_child (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + return FALSE; +} + +static int +gtk_list_store_iter_n_children (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + GtkListStore *list_store = GTK_LIST_STORE (tree_model); + GtkListStorePrivate *priv = list_store->priv; + + if (iter == NULL) + return g_sequence_get_length (priv->seq); + + g_return_val_if_fail (priv->stamp == iter->stamp, -1); + + return 0; +} + +static gboolean +gtk_list_store_iter_nth_child (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent, + int n) +{ + GtkListStore *list_store = GTK_LIST_STORE (tree_model); + GtkListStorePrivate *priv = list_store->priv; + GSequenceIter *child; + + iter->stamp = 0; + + if (parent) + return FALSE; + + child = g_sequence_get_iter_at_pos (priv->seq, n); + + if (g_sequence_iter_is_end (child)) + return FALSE; + + iter->stamp = priv->stamp; + iter->user_data = child; + + return TRUE; +} + +static gboolean +gtk_list_store_iter_parent (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *child) +{ + iter->stamp = 0; + return FALSE; +} + +static gboolean +gtk_list_store_real_set_value (GtkListStore *list_store, + GtkTreeIter *iter, + int column, + GValue *value, + gboolean sort) +{ + GtkListStorePrivate *priv = list_store->priv; + GtkTreeDataList *list; + GtkTreeDataList *prev; + int old_column = column; + GValue real_value = G_VALUE_INIT; + gboolean converted = FALSE; + gboolean retval = FALSE; + + if (! g_type_is_a (G_VALUE_TYPE (value), priv->column_headers[column])) + { + if (! (g_value_type_transformable (G_VALUE_TYPE (value), priv->column_headers[column]))) + { + g_warning ("%s: Unable to convert from %s to %s", + G_STRLOC, + g_type_name (G_VALUE_TYPE (value)), + g_type_name (priv->column_headers[column])); + return retval; + } + + g_value_init (&real_value, priv->column_headers[column]); + if (!g_value_transform (value, &real_value)) + { + g_warning ("%s: Unable to make conversion from %s to %s", + G_STRLOC, + g_type_name (G_VALUE_TYPE (value)), + g_type_name (priv->column_headers[column])); + g_value_unset (&real_value); + return retval; + } + converted = TRUE; + } + + prev = list = g_sequence_get (iter->user_data); + + while (list != NULL) + { + if (column == 0) + { + if (converted) + _gtk_tree_data_list_value_to_node (list, &real_value); + else + _gtk_tree_data_list_value_to_node (list, value); + retval = TRUE; + if (converted) + g_value_unset (&real_value); + if (sort && GTK_LIST_STORE_IS_SORTED (list_store)) + gtk_list_store_sort_iter_changed (list_store, iter, old_column); + return retval; + } + + column--; + prev = list; + list = list->next; + } + + if (g_sequence_get (iter->user_data) == NULL) + { + list = _gtk_tree_data_list_alloc(); + g_sequence_set (iter->user_data, list); + list->next = NULL; + } + else + { + list = prev->next = _gtk_tree_data_list_alloc (); + list->next = NULL; + } + + while (column != 0) + { + list->next = _gtk_tree_data_list_alloc (); + list = list->next; + list->next = NULL; + column --; + } + + if (converted) + _gtk_tree_data_list_value_to_node (list, &real_value); + else + _gtk_tree_data_list_value_to_node (list, value); + + retval = TRUE; + if (converted) + g_value_unset (&real_value); + + if (sort && GTK_LIST_STORE_IS_SORTED (list_store)) + gtk_list_store_sort_iter_changed (list_store, iter, old_column); + + return retval; +} + + +/** + * gtk_list_store_set_value: + * @list_store: A `GtkListStore` + * @iter: A valid `GtkTreeIter` for the row being modified + * @column: column number to modify + * @value: new value for the cell + * + * Sets the data in the cell specified by @iter and @column. + * The type of @value must be convertible to the type of the + * column. + * + * Deprecated: 4.10: Use list models + **/ +void +gtk_list_store_set_value (GtkListStore *list_store, + GtkTreeIter *iter, + int column, + GValue *value) +{ + GtkListStorePrivate *priv; + + g_return_if_fail (GTK_IS_LIST_STORE (list_store)); + g_return_if_fail (iter_is_valid (iter, list_store)); + g_return_if_fail (G_IS_VALUE (value)); + priv = list_store->priv; + g_return_if_fail (column >= 0 && column < priv->n_columns); + + if (gtk_list_store_real_set_value (list_store, iter, column, value, TRUE)) + { + GtkTreePath *path; + + path = gtk_list_store_get_path (GTK_TREE_MODEL (list_store), iter); + gtk_tree_model_row_changed (GTK_TREE_MODEL (list_store), path, iter); + gtk_tree_path_free (path); + } +} + +static GtkTreeIterCompareFunc +gtk_list_store_get_compare_func (GtkListStore *list_store) +{ + GtkListStorePrivate *priv = list_store->priv; + GtkTreeIterCompareFunc func = NULL; + + if (GTK_LIST_STORE_IS_SORTED (list_store)) + { + if (priv->sort_column_id != -1) + { + GtkTreeDataSortHeader *header; + header = _gtk_tree_data_list_get_header (priv->sort_list, + priv->sort_column_id); + g_return_val_if_fail (header != NULL, NULL); + g_return_val_if_fail (header->func != NULL, NULL); + func = header->func; + } + else + { + func = priv->default_sort_func; + } + } + + return func; +} + +static void +gtk_list_store_set_vector_internal (GtkListStore *list_store, + GtkTreeIter *iter, + gboolean *emit_signal, + gboolean *maybe_need_sort, + int *columns, + GValue *values, + int n_values) +{ + GtkListStorePrivate *priv = list_store->priv; + int i; + GtkTreeIterCompareFunc func = NULL; + + func = gtk_list_store_get_compare_func (list_store); + if (func != _gtk_tree_data_list_compare_func) + *maybe_need_sort = TRUE; + + for (i = 0; i < n_values; i++) + { + *emit_signal = gtk_list_store_real_set_value (list_store, + iter, + columns[i], + &values[i], + FALSE) || *emit_signal; + + if (func == _gtk_tree_data_list_compare_func && + columns[i] == priv->sort_column_id) + *maybe_need_sort = TRUE; + } +} + +static void +gtk_list_store_set_valist_internal (GtkListStore *list_store, + GtkTreeIter *iter, + gboolean *emit_signal, + gboolean *maybe_need_sort, + va_list var_args) +{ + GtkListStorePrivate *priv = list_store->priv; + int column; + GtkTreeIterCompareFunc func = NULL; + + column = va_arg (var_args, int); + + func = gtk_list_store_get_compare_func (list_store); + if (func != _gtk_tree_data_list_compare_func) + *maybe_need_sort = TRUE; + + while (column != -1) + { + GValue value = G_VALUE_INIT; + char *error = NULL; + + if (column < 0 || column >= priv->n_columns) + { + g_warning ("%s: Invalid column number %d added to iter (remember to end your list of columns with a -1)", G_STRLOC, column); + break; + } + + G_VALUE_COLLECT_INIT (&value, priv->column_headers[column], + var_args, 0, &error); + if (error) + { + g_warning ("%s: %s", G_STRLOC, error); + g_free (error); + + /* we purposely leak the value here, it might not be + * in a sane state if an error condition occurred + */ + break; + } + + /* FIXME: instead of calling this n times, refactor with above */ + *emit_signal = gtk_list_store_real_set_value (list_store, + iter, + column, + &value, + FALSE) || *emit_signal; + + if (func == _gtk_tree_data_list_compare_func && + column == priv->sort_column_id) + *maybe_need_sort = TRUE; + + g_value_unset (&value); + + column = va_arg (var_args, int); + } +} + +/** + * gtk_list_store_set_valuesv: (rename-to gtk_list_store_set) + * @list_store: A `GtkListStore` + * @iter: A valid `GtkTreeIter` for the row being modified + * @columns: (array length=n_values): an array of column numbers + * @values: (array length=n_values): an array of GValues + * @n_values: the length of the @columns and @values arrays + * + * A variant of gtk_list_store_set_valist() which + * takes the columns and values as two arrays, instead of + * varargs. This function is mainly intended for + * language-bindings and in case the number of columns to + * change is not known until run-time. + * + * Deprecated: 4.10: Use list models + */ +void +gtk_list_store_set_valuesv (GtkListStore *list_store, + GtkTreeIter *iter, + int *columns, + GValue *values, + int n_values) +{ + GtkListStorePrivate *priv; + gboolean emit_signal = FALSE; + gboolean maybe_need_sort = FALSE; + + g_return_if_fail (GTK_IS_LIST_STORE (list_store)); + g_return_if_fail (iter_is_valid (iter, list_store)); + + priv = list_store->priv; + + gtk_list_store_set_vector_internal (list_store, iter, + &emit_signal, + &maybe_need_sort, + columns, values, n_values); + + if (maybe_need_sort && GTK_LIST_STORE_IS_SORTED (list_store)) + gtk_list_store_sort_iter_changed (list_store, iter, priv->sort_column_id); + + if (emit_signal) + { + GtkTreePath *path; + + path = gtk_list_store_get_path (GTK_TREE_MODEL (list_store), iter); + gtk_tree_model_row_changed (GTK_TREE_MODEL (list_store), path, iter); + gtk_tree_path_free (path); + } +} + +/** + * gtk_list_store_set_valist: + * @list_store: A `GtkListStore` + * @iter: A valid `GtkTreeIter` for the row being modified + * @var_args: va_list of column/value pairs + * + * See gtk_list_store_set(); this version takes a va_list for use by language + * bindings. + * + * Deprecated: 4.10: Use list models + **/ +void +gtk_list_store_set_valist (GtkListStore *list_store, + GtkTreeIter *iter, + va_list var_args) +{ + GtkListStorePrivate *priv; + gboolean emit_signal = FALSE; + gboolean maybe_need_sort = FALSE; + + g_return_if_fail (GTK_IS_LIST_STORE (list_store)); + g_return_if_fail (iter_is_valid (iter, list_store)); + + priv = list_store->priv; + + gtk_list_store_set_valist_internal (list_store, iter, + &emit_signal, + &maybe_need_sort, + var_args); + + if (maybe_need_sort && GTK_LIST_STORE_IS_SORTED (list_store)) + gtk_list_store_sort_iter_changed (list_store, iter, priv->sort_column_id); + + if (emit_signal) + { + GtkTreePath *path; + + path = gtk_list_store_get_path (GTK_TREE_MODEL (list_store), iter); + gtk_tree_model_row_changed (GTK_TREE_MODEL (list_store), path, iter); + gtk_tree_path_free (path); + } +} + +/** + * gtk_list_store_set: + * @list_store: a `GtkListStore` + * @iter: row iterator + * @...: pairs of column number and value, terminated with -1 + * + * Sets the value of one or more cells in the row referenced by @iter. + * The variable argument list should contain integer column numbers, + * each column number followed by the value to be set. + * The list is terminated by a -1. For example, to set column 0 with type + * %G_TYPE_STRING to “Foo”, you would write `gtk_list_store_set (store, iter, + * 0, "Foo", -1)`. + * + * The value will be referenced by the store if it is a %G_TYPE_OBJECT, and it + * will be copied if it is a %G_TYPE_STRING or %G_TYPE_BOXED. + * + * Deprecated: 4.10: Use list models + */ +void +gtk_list_store_set (GtkListStore *list_store, + GtkTreeIter *iter, + ...) +{ + va_list var_args; + + va_start (var_args, iter); + gtk_list_store_set_valist (list_store, iter, var_args); + va_end (var_args); +} + +/** + * gtk_list_store_remove: + * @list_store: A `GtkListStore` + * @iter: A valid `GtkTreeIter` + * + * Removes the given row from the list store. After being removed, + * @iter is set to be the next valid row, or invalidated if it pointed + * to the last row in @list_store. + * + * Returns: %TRUE if @iter is valid, %FALSE if not. + * + * Deprecated: 4.10: Use list models + **/ +gboolean +gtk_list_store_remove (GtkListStore *list_store, + GtkTreeIter *iter) +{ + GtkListStorePrivate *priv; + GtkTreePath *path; + GSequenceIter *ptr, *next; + + g_return_val_if_fail (GTK_IS_LIST_STORE (list_store), FALSE); + g_return_val_if_fail (iter_is_valid (iter, list_store), FALSE); + + priv = list_store->priv; + + path = gtk_list_store_get_path (GTK_TREE_MODEL (list_store), iter); + + ptr = iter->user_data; + next = g_sequence_iter_next (ptr); + + _gtk_tree_data_list_free (g_sequence_get (ptr), priv->column_headers); + g_sequence_remove (iter->user_data); + + priv->length--; + + gtk_tree_model_row_deleted (GTK_TREE_MODEL (list_store), path); + gtk_tree_path_free (path); + + if (g_sequence_iter_is_end (next)) + { + iter->stamp = 0; + return FALSE; + } + else + { + iter->stamp = priv->stamp; + iter->user_data = next; + return TRUE; + } +} + +/** + * gtk_list_store_insert: + * @list_store: A `GtkListStore` + * @iter: (out): An unset `GtkTreeIter` to set to the new row + * @position: position to insert the new row, or -1 for last + * + * Creates a new row at @position. @iter will be changed to point to this new + * row. If @position is -1 or is larger than the number of rows on the list, + * then the new row will be appended to the list. The row will be empty after + * this function is called. To fill in values, you need to call + * gtk_list_store_set() or gtk_list_store_set_value(). + * + * Deprecated: 4.10: Use list models + **/ +void +gtk_list_store_insert (GtkListStore *list_store, + GtkTreeIter *iter, + int position) +{ + GtkListStorePrivate *priv; + GtkTreePath *path; + GSequence *seq; + GSequenceIter *ptr; + int length; + + g_return_if_fail (GTK_IS_LIST_STORE (list_store)); + g_return_if_fail (iter != NULL); + + priv = list_store->priv; + + priv->columns_dirty = TRUE; + + seq = priv->seq; + + length = g_sequence_get_length (seq); + if (position > length || position < 0) + position = length; + + ptr = g_sequence_get_iter_at_pos (seq, position); + ptr = g_sequence_insert_before (ptr, NULL); + + iter->stamp = priv->stamp; + iter->user_data = ptr; + + g_assert (iter_is_valid (iter, list_store)); + + priv->length++; + + path = gtk_tree_path_new (); + gtk_tree_path_append_index (path, position); + gtk_tree_model_row_inserted (GTK_TREE_MODEL (list_store), path, iter); + gtk_tree_path_free (path); +} + +/** + * gtk_list_store_insert_before: + * @list_store: A `GtkListStore` + * @iter: (out): An unset `GtkTreeIter` to set to the new row + * @sibling: (nullable): A valid `GtkTreeIter` + * + * Inserts a new row before @sibling. If @sibling is %NULL, then the row will + * be appended to the end of the list. @iter will be changed to point to this + * new row. The row will be empty after this function is called. To fill in + * values, you need to call gtk_list_store_set() or gtk_list_store_set_value(). + * + * Deprecated: 4.10: Use list models + **/ +void +gtk_list_store_insert_before (GtkListStore *list_store, + GtkTreeIter *iter, + GtkTreeIter *sibling) +{ + GtkListStorePrivate *priv; + GSequenceIter *after; + + g_return_if_fail (GTK_IS_LIST_STORE (list_store)); + g_return_if_fail (iter != NULL); + + priv = list_store->priv; + + if (sibling) + g_return_if_fail (iter_is_valid (sibling, list_store)); + + if (!sibling) + after = g_sequence_get_end_iter (priv->seq); + else + after = sibling->user_data; + + gtk_list_store_insert (list_store, iter, g_sequence_iter_get_position (after)); +} + +/** + * gtk_list_store_insert_after: + * @list_store: A `GtkListStore` + * @iter: (out): An unset `GtkTreeIter` to set to the new row + * @sibling: (nullable): A valid `GtkTreeIter` + * + * Inserts a new row after @sibling. If @sibling is %NULL, then the row will be + * prepended to the beginning of the list. @iter will be changed to point to + * this new row. The row will be empty after this function is called. To fill + * in values, you need to call gtk_list_store_set() or gtk_list_store_set_value(). + * + * Deprecated: 4.10: Use list models + **/ +void +gtk_list_store_insert_after (GtkListStore *list_store, + GtkTreeIter *iter, + GtkTreeIter *sibling) +{ + GtkListStorePrivate *priv; + GSequenceIter *after; + + g_return_if_fail (GTK_IS_LIST_STORE (list_store)); + g_return_if_fail (iter != NULL); + + priv = list_store->priv; + + if (sibling) + g_return_if_fail (iter_is_valid (sibling, list_store)); + + if (!sibling) + after = g_sequence_get_begin_iter (priv->seq); + else + after = g_sequence_iter_next (sibling->user_data); + + gtk_list_store_insert (list_store, iter, g_sequence_iter_get_position (after)); +} + +/** + * gtk_list_store_prepend: + * @list_store: A `GtkListStore` + * @iter: (out): An unset `GtkTreeIter` to set to the prepend row + * + * Prepends a new row to @list_store. @iter will be changed to point to this new + * row. The row will be empty after this function is called. To fill in + * values, you need to call gtk_list_store_set() or gtk_list_store_set_value(). + * + * Deprecated: 4.10: Use list models + **/ +void +gtk_list_store_prepend (GtkListStore *list_store, + GtkTreeIter *iter) +{ + g_return_if_fail (GTK_IS_LIST_STORE (list_store)); + g_return_if_fail (iter != NULL); + + gtk_list_store_insert (list_store, iter, 0); +} + +/** + * gtk_list_store_append: + * @list_store: A `GtkListStore` + * @iter: (out): An unset `GtkTreeIter` to set to the appended row + * + * Appends a new row to @list_store. @iter will be changed to point to this new + * row. The row will be empty after this function is called. To fill in + * values, you need to call gtk_list_store_set() or gtk_list_store_set_value(). + * + * Deprecated: 4.10: Use list models + **/ +void +gtk_list_store_append (GtkListStore *list_store, + GtkTreeIter *iter) +{ + g_return_if_fail (GTK_IS_LIST_STORE (list_store)); + g_return_if_fail (iter != NULL); + + gtk_list_store_insert (list_store, iter, -1); +} + +static void +gtk_list_store_increment_stamp (GtkListStore *list_store) +{ + GtkListStorePrivate *priv = list_store->priv; + + do + { + priv->stamp++; + } + while (priv->stamp == 0); +} + +/** + * gtk_list_store_clear: + * @list_store: a `GtkListStore`. + * + * Removes all rows from the list store. + * + * Deprecated: 4.10: Use list models + **/ +void +gtk_list_store_clear (GtkListStore *list_store) +{ + GtkListStorePrivate *priv; + GtkTreeIter iter; + + g_return_if_fail (GTK_IS_LIST_STORE (list_store)); + + priv = list_store->priv; + + while (g_sequence_get_length (priv->seq) > 0) + { + iter.stamp = priv->stamp; + iter.user_data = g_sequence_get_begin_iter (priv->seq); + gtk_list_store_remove (list_store, &iter); + } + + gtk_list_store_increment_stamp (list_store); +} + +/** + * gtk_list_store_iter_is_valid: + * @list_store: a list store + * @iter: the iterator to check + * + * Checks if the given iter is a valid iter for this `GtkListStore`. + * + * This function is slow. Only use it for debugging and/or testing + * purposes. + * + * Returns: %TRUE if the iter is valid, %FALSE if the iter is invalid. + * + * Deprecated: 4.10: Use list models + **/ +gboolean +gtk_list_store_iter_is_valid (GtkListStore *list_store, + GtkTreeIter *iter) +{ + GtkListStorePrivate *priv; + GSequenceIter *seq_iter; + + g_return_val_if_fail (GTK_IS_LIST_STORE (list_store), FALSE); + g_return_val_if_fail (iter != NULL, FALSE); + + /* can't use iter_is_valid() here, because iter might point + * to random memory. + * + * We MUST NOT dereference it. + */ + + priv = list_store->priv; + + if (iter == NULL || + iter->user_data == NULL || + priv->stamp != iter->stamp) + return FALSE; + + for (seq_iter = g_sequence_get_begin_iter (priv->seq); + !g_sequence_iter_is_end (seq_iter); + seq_iter = g_sequence_iter_next (seq_iter)) + { + if (seq_iter == iter->user_data) + return TRUE; + } + + return FALSE; +} + +static gboolean real_gtk_list_store_row_draggable (GtkTreeDragSource *drag_source, + GtkTreePath *path) +{ + return TRUE; +} + +static gboolean +gtk_list_store_drag_data_delete (GtkTreeDragSource *drag_source, + GtkTreePath *path) +{ + GtkTreeIter iter; + + if (gtk_list_store_get_iter (GTK_TREE_MODEL (drag_source), + &iter, + path)) + { + gtk_list_store_remove (GTK_LIST_STORE (drag_source), &iter); + return TRUE; + } + return FALSE; +} + +static GdkContentProvider * +gtk_list_store_drag_data_get (GtkTreeDragSource *drag_source, + GtkTreePath *path) +{ + /* Note that we don't need to handle the GTK_TREE_MODEL_ROW + * target, because the default handler does it for us, but + * we do anyway for the convenience of someone maybe overriding the + * default handler. + */ + return gtk_tree_create_row_drag_content (GTK_TREE_MODEL (drag_source), path); +} + +static gboolean +gtk_list_store_drag_data_received (GtkTreeDragDest *drag_dest, + GtkTreePath *dest, + const GValue *value) +{ + GtkTreeModel *tree_model = GTK_TREE_MODEL (drag_dest); + GtkListStore *list_store = GTK_LIST_STORE (tree_model); + GtkListStorePrivate *priv = list_store->priv; + GtkTreeModel *src_model = NULL; + GtkTreePath *src_path = NULL; + gboolean retval = FALSE; + + if (gtk_tree_get_row_drag_data (value, + &src_model, + &src_path) && + src_model == tree_model) + { + /* Copy the given row to a new position */ + GtkTreeIter src_iter; + GtkTreeIter dest_iter; + GtkTreePath *prev; + + if (!gtk_list_store_get_iter (src_model, + &src_iter, + src_path)) + { + goto out; + } + + /* Get the path to insert _after_ (dest is the path to insert _before_) */ + prev = gtk_tree_path_copy (dest); + + if (!gtk_tree_path_prev (prev)) + { + /* dest was the first spot in the list; which means we are supposed + * to prepend. + */ + gtk_list_store_prepend (list_store, &dest_iter); + + retval = TRUE; + } + else + { + if (gtk_list_store_get_iter (tree_model, &dest_iter, prev)) + { + GtkTreeIter tmp_iter = dest_iter; + + gtk_list_store_insert_after (list_store, &dest_iter, &tmp_iter); + + retval = TRUE; + } + } + + gtk_tree_path_free (prev); + + /* If we succeeded in creating dest_iter, copy data from src + */ + if (retval) + { + GtkTreeDataList *dl = g_sequence_get (src_iter.user_data); + GtkTreeDataList *copy_head = NULL; + GtkTreeDataList *copy_prev = NULL; + GtkTreeDataList *copy_iter = NULL; + GtkTreePath *path; + int col; + + col = 0; + while (dl) + { + copy_iter = _gtk_tree_data_list_node_copy (dl, + priv->column_headers[col]); + + if (copy_head == NULL) + copy_head = copy_iter; + + if (copy_prev) + copy_prev->next = copy_iter; + + copy_prev = copy_iter; + + dl = dl->next; + ++col; + } + + dest_iter.stamp = priv->stamp; + g_sequence_set (dest_iter.user_data, copy_head); + + path = gtk_list_store_get_path (tree_model, &dest_iter); + gtk_tree_model_row_changed (tree_model, path, &dest_iter); + gtk_tree_path_free (path); + } + } + else + { + /* FIXME maybe add some data targets eventually, or handle text + * targets in the simple case. + */ + } + + out: + + if (src_path) + gtk_tree_path_free (src_path); + + return retval; +} + +static gboolean +gtk_list_store_row_drop_possible (GtkTreeDragDest *drag_dest, + GtkTreePath *dest_path, + const GValue *value) +{ + int *indices; + GtkTreeModel *src_model = NULL; + GtkTreePath *src_path = NULL; + gboolean retval = FALSE; + + /* don't accept drops if the list has been sorted */ + if (GTK_LIST_STORE_IS_SORTED (drag_dest)) + return FALSE; + + if (!gtk_tree_get_row_drag_data (value, + &src_model, + &src_path)) + goto out; + + if (src_model != GTK_TREE_MODEL (drag_dest)) + goto out; + + if (gtk_tree_path_get_depth (dest_path) != 1) + goto out; + + /* can drop before any existing node, or before one past any existing. */ + + indices = gtk_tree_path_get_indices (dest_path); + + if (indices[0] <= g_sequence_get_length (GTK_LIST_STORE (drag_dest)->priv->seq)) + retval = TRUE; + + out: + if (src_path) + gtk_tree_path_free (src_path); + + return retval; +} + +/* Sorting and reordering */ + +/* Reordering */ +static int +gtk_list_store_reorder_func (GSequenceIter *a, + GSequenceIter *b, + gpointer user_data) +{ + GHashTable *new_positions = user_data; + int apos = GPOINTER_TO_INT (g_hash_table_lookup (new_positions, a)); + int bpos = GPOINTER_TO_INT (g_hash_table_lookup (new_positions, b)); + + if (apos < bpos) + return -1; + if (apos > bpos) + return 1; + return 0; +} + +/** + * gtk_list_store_reorder: + * @store: A `GtkListStore`. + * @new_order: (array zero-terminated=1): an array of integers mapping the new + * position of each child to its old position before the re-ordering, + * i.e. @new_order`[newpos] = oldpos`. It must have + * exactly as many items as the list store’s length. + * + * Reorders @store to follow the order indicated by @new_order. Note that + * this function only works with unsorted stores. + * + * Deprecated: 4.10: Use list models + **/ +void +gtk_list_store_reorder (GtkListStore *store, + int *new_order) +{ + GtkListStorePrivate *priv; + int i; + GtkTreePath *path; + GHashTable *new_positions; + GSequenceIter *ptr; + int *order; + + g_return_if_fail (GTK_IS_LIST_STORE (store)); + g_return_if_fail (!GTK_LIST_STORE_IS_SORTED (store)); + g_return_if_fail (new_order != NULL); + + priv = store->priv; + + order = g_new (int, g_sequence_get_length (priv->seq)); + for (i = 0; i < g_sequence_get_length (priv->seq); i++) + order[new_order[i]] = i; + + new_positions = g_hash_table_new (g_direct_hash, g_direct_equal); + + ptr = g_sequence_get_begin_iter (priv->seq); + i = 0; + while (!g_sequence_iter_is_end (ptr)) + { + g_hash_table_insert (new_positions, ptr, GINT_TO_POINTER (order[i++])); + + ptr = g_sequence_iter_next (ptr); + } + g_free (order); + + g_sequence_sort_iter (priv->seq, gtk_list_store_reorder_func, new_positions); + + g_hash_table_destroy (new_positions); + + /* emit signal */ + path = gtk_tree_path_new (); + gtk_tree_model_rows_reordered (GTK_TREE_MODEL (store), + path, NULL, new_order); + gtk_tree_path_free (path); +} + +static GHashTable * +save_positions (GSequence *seq) +{ + GHashTable *positions = g_hash_table_new (g_direct_hash, g_direct_equal); + GSequenceIter *ptr; + + ptr = g_sequence_get_begin_iter (seq); + while (!g_sequence_iter_is_end (ptr)) + { + g_hash_table_insert (positions, ptr, + GINT_TO_POINTER (g_sequence_iter_get_position (ptr))); + ptr = g_sequence_iter_next (ptr); + } + + return positions; +} + +static int * +generate_order (GSequence *seq, + GHashTable *old_positions) +{ + GSequenceIter *ptr; + int *order = g_new (int, g_sequence_get_length (seq)); + int i; + + i = 0; + ptr = g_sequence_get_begin_iter (seq); + while (!g_sequence_iter_is_end (ptr)) + { + int old_pos = GPOINTER_TO_INT (g_hash_table_lookup (old_positions, ptr)); + order[i++] = old_pos; + ptr = g_sequence_iter_next (ptr); + } + + g_hash_table_destroy (old_positions); + + return order; +} + +/** + * gtk_list_store_swap: + * @store: A `GtkListStore`. + * @a: A `GtkTreeIter` + * @b: Another `GtkTreeIter` + * + * Swaps @a and @b in @store. Note that this function only works with + * unsorted stores. + * + * Deprecated: 4.10: Use list models + **/ +void +gtk_list_store_swap (GtkListStore *store, + GtkTreeIter *a, + GtkTreeIter *b) +{ + GtkListStorePrivate *priv; + GHashTable *old_positions; + int *order; + GtkTreePath *path; + + g_return_if_fail (GTK_IS_LIST_STORE (store)); + g_return_if_fail (!GTK_LIST_STORE_IS_SORTED (store)); + g_return_if_fail (iter_is_valid (a, store)); + g_return_if_fail (iter_is_valid (b, store)); + + priv = store->priv; + + if (a->user_data == b->user_data) + return; + + old_positions = save_positions (priv->seq); + + g_sequence_swap (a->user_data, b->user_data); + + order = generate_order (priv->seq, old_positions); + path = gtk_tree_path_new (); + + gtk_tree_model_rows_reordered (GTK_TREE_MODEL (store), + path, NULL, order); + + gtk_tree_path_free (path); + g_free (order); +} + +static void +gtk_list_store_move_to (GtkListStore *store, + GtkTreeIter *iter, + int new_pos) +{ + GtkListStorePrivate *priv = store->priv; + GHashTable *old_positions; + GtkTreePath *path; + int *order; + + old_positions = save_positions (priv->seq); + + g_sequence_move (iter->user_data, g_sequence_get_iter_at_pos (priv->seq, new_pos)); + + order = generate_order (priv->seq, old_positions); + + path = gtk_tree_path_new (); + gtk_tree_model_rows_reordered (GTK_TREE_MODEL (store), + path, NULL, order); + gtk_tree_path_free (path); + g_free (order); +} + +/** + * gtk_list_store_move_before: + * @store: A `GtkListStore`. + * @iter: A `GtkTreeIter` + * @position: (nullable): A `GtkTreeIter` + * + * Moves @iter in @store to the position before @position. Note that this + * function only works with unsorted stores. If @position is %NULL, @iter + * will be moved to the end of the list. + * + * Deprecated: 4.10: Use list models + **/ +void +gtk_list_store_move_before (GtkListStore *store, + GtkTreeIter *iter, + GtkTreeIter *position) +{ + int pos; + + g_return_if_fail (GTK_IS_LIST_STORE (store)); + g_return_if_fail (!GTK_LIST_STORE_IS_SORTED (store)); + g_return_if_fail (iter_is_valid (iter, store)); + if (position) + g_return_if_fail (iter_is_valid (position, store)); + + if (position) + pos = g_sequence_iter_get_position (position->user_data); + else + pos = -1; + + gtk_list_store_move_to (store, iter, pos); +} + +/** + * gtk_list_store_move_after: + * @store: A `GtkListStore`. + * @iter: A `GtkTreeIter` + * @position: (nullable): A `GtkTreeIter` + * + * Moves @iter in @store to the position after @position. Note that this + * function only works with unsorted stores. If @position is %NULL, @iter + * will be moved to the start of the list. + * + * Deprecated: 4.10: Use list models + **/ +void +gtk_list_store_move_after (GtkListStore *store, + GtkTreeIter *iter, + GtkTreeIter *position) +{ + int pos; + + g_return_if_fail (GTK_IS_LIST_STORE (store)); + g_return_if_fail (!GTK_LIST_STORE_IS_SORTED (store)); + g_return_if_fail (iter_is_valid (iter, store)); + if (position) + g_return_if_fail (iter_is_valid (position, store)); + + if (position) + pos = g_sequence_iter_get_position (position->user_data) + 1; + else + pos = 0; + + gtk_list_store_move_to (store, iter, pos); +} + +/* Sorting */ +static int +gtk_list_store_compare_func (GSequenceIter *a, + GSequenceIter *b, + gpointer user_data) +{ + GtkListStore *list_store = user_data; + GtkListStorePrivate *priv = list_store->priv; + GtkTreeIter iter_a; + GtkTreeIter iter_b; + int retval; + GtkTreeIterCompareFunc func; + gpointer data; + + if (priv->sort_column_id != -1) + { + GtkTreeDataSortHeader *header; + + header = _gtk_tree_data_list_get_header (priv->sort_list, + priv->sort_column_id); + g_return_val_if_fail (header != NULL, 0); + g_return_val_if_fail (header->func != NULL, 0); + + func = header->func; + data = header->data; + } + else + { + g_return_val_if_fail (priv->default_sort_func != NULL, 0); + func = priv->default_sort_func; + data = priv->default_sort_data; + } + + iter_a.stamp = priv->stamp; + iter_a.user_data = (gpointer)a; + iter_b.stamp = priv->stamp; + iter_b.user_data = (gpointer)b; + + g_assert (iter_is_valid (&iter_a, list_store)); + g_assert (iter_is_valid (&iter_b, list_store)); + + retval = (* func) (GTK_TREE_MODEL (list_store), &iter_a, &iter_b, data); + + if (priv->order == GTK_SORT_DESCENDING) + { + if (retval > 0) + retval = -1; + else if (retval < 0) + retval = 1; + } + + return retval; +} + +static void +gtk_list_store_sort (GtkListStore *list_store) +{ + GtkListStorePrivate *priv = list_store->priv; + int *new_order; + GtkTreePath *path; + GHashTable *old_positions; + + if (!GTK_LIST_STORE_IS_SORTED (list_store) || + g_sequence_get_length (priv->seq) <= 1) + return; + + old_positions = save_positions (priv->seq); + + g_sequence_sort_iter (priv->seq, gtk_list_store_compare_func, list_store); + + /* Let the world know about our new order */ + new_order = generate_order (priv->seq, old_positions); + + path = gtk_tree_path_new (); + gtk_tree_model_rows_reordered (GTK_TREE_MODEL (list_store), + path, NULL, new_order); + gtk_tree_path_free (path); + g_free (new_order); +} + +static gboolean +iter_is_sorted (GtkListStore *list_store, + GtkTreeIter *iter) +{ + GSequenceIter *cmp; + + if (!g_sequence_iter_is_begin (iter->user_data)) + { + cmp = g_sequence_iter_prev (iter->user_data); + if (gtk_list_store_compare_func (cmp, iter->user_data, list_store) > 0) + return FALSE; + } + + cmp = g_sequence_iter_next (iter->user_data); + if (!g_sequence_iter_is_end (cmp)) + { + if (gtk_list_store_compare_func (iter->user_data, cmp, list_store) > 0) + return FALSE; + } + + return TRUE; +} + +static void +gtk_list_store_sort_iter_changed (GtkListStore *list_store, + GtkTreeIter *iter, + int column) + +{ + GtkListStorePrivate *priv = list_store->priv; + GtkTreePath *path; + + path = gtk_list_store_get_path (GTK_TREE_MODEL (list_store), iter); + gtk_tree_model_row_changed (GTK_TREE_MODEL (list_store), path, iter); + gtk_tree_path_free (path); + + if (!iter_is_sorted (list_store, iter)) + { + GHashTable *old_positions; + int *order; + + old_positions = save_positions (priv->seq); + g_sequence_sort_changed_iter (iter->user_data, + gtk_list_store_compare_func, + list_store); + order = generate_order (priv->seq, old_positions); + path = gtk_tree_path_new (); + gtk_tree_model_rows_reordered (GTK_TREE_MODEL (list_store), + path, NULL, order); + gtk_tree_path_free (path); + g_free (order); + } +} + +static gboolean +gtk_list_store_get_sort_column_id (GtkTreeSortable *sortable, + int *sort_column_id, + GtkSortType *order) +{ + GtkListStore *list_store = GTK_LIST_STORE (sortable); + GtkListStorePrivate *priv = list_store->priv; + + if (sort_column_id) + * sort_column_id = priv->sort_column_id; + if (order) + * order = priv->order; + + if (priv->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID || + priv->sort_column_id == GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID) + return FALSE; + + return TRUE; +} + +static void +gtk_list_store_set_sort_column_id (GtkTreeSortable *sortable, + int sort_column_id, + GtkSortType order) +{ + GtkListStore *list_store = GTK_LIST_STORE (sortable); + GtkListStorePrivate *priv = list_store->priv; + + if ((priv->sort_column_id == sort_column_id) && + (priv->order == order)) + return; + + if (sort_column_id != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID) + { + if (sort_column_id != GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID) + { + GtkTreeDataSortHeader *header = NULL; + + header = _gtk_tree_data_list_get_header (priv->sort_list, + sort_column_id); + + /* We want to make sure that we have a function */ + g_return_if_fail (header != NULL); + g_return_if_fail (header->func != NULL); + } + else + { + g_return_if_fail (priv->default_sort_func != NULL); + } + } + + + priv->sort_column_id = sort_column_id; + priv->order = order; + + gtk_tree_sortable_sort_column_changed (sortable); + + gtk_list_store_sort (list_store); +} + +static void +gtk_list_store_set_sort_func (GtkTreeSortable *sortable, + int sort_column_id, + GtkTreeIterCompareFunc func, + gpointer data, + GDestroyNotify destroy) +{ + GtkListStore *list_store = GTK_LIST_STORE (sortable); + GtkListStorePrivate *priv = list_store->priv; + + priv->sort_list = _gtk_tree_data_list_set_header (priv->sort_list, + sort_column_id, + func, data, destroy); + + if (priv->sort_column_id == sort_column_id) + gtk_list_store_sort (list_store); +} + +static void +gtk_list_store_set_default_sort_func (GtkTreeSortable *sortable, + GtkTreeIterCompareFunc func, + gpointer data, + GDestroyNotify destroy) +{ + GtkListStore *list_store = GTK_LIST_STORE (sortable); + GtkListStorePrivate *priv = list_store->priv; + + if (priv->default_sort_destroy) + { + GDestroyNotify d = priv->default_sort_destroy; + + priv->default_sort_destroy = NULL; + d (priv->default_sort_data); + } + + priv->default_sort_func = func; + priv->default_sort_data = data; + priv->default_sort_destroy = destroy; + + if (priv->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID) + gtk_list_store_sort (list_store); +} + +static gboolean +gtk_list_store_has_default_sort_func (GtkTreeSortable *sortable) +{ + GtkListStore *list_store = GTK_LIST_STORE (sortable); + GtkListStorePrivate *priv = list_store->priv; + + return (priv->default_sort_func != NULL); +} + + +/** + * gtk_list_store_insert_with_values: + * @list_store: A `GtkListStore` + * @iter: (out) (optional): An unset `GtkTreeIter` to set to the new row + * @position: position to insert the new row, or -1 to append after existing + * rows + * @...: pairs of column number and value, terminated with -1 + * + * Creates a new row at @position. @iter will be changed to point to this new + * row. If @position is -1, or larger than the number of rows in the list, then + * the new row will be appended to the list. The row will be filled with the + * values given to this function. + * + * Calling + * `gtk_list_store_insert_with_values (list_store, iter, position...)` + * has the same effect as calling: + * + * |[ + * static void + * insert_value (GtkListStore *list_store, + * GtkTreeIter *iter, + * int position) + * { + * gtk_list_store_insert (list_store, iter, position); + * gtk_list_store_set (list_store, + * iter + * // ... + * ); + * } + * ]| + * + * with the difference that the former will only emit `GtkTreeModel`::row-inserted + * once, while the latter will emit `GtkTreeModel`::row-inserted, + * `GtkTreeModel`::row-changed and, if the list store is sorted, + * `GtkTreeModel`::rows-reordered for every inserted value. + * + * Since emitting the `GtkTreeModel::rows-reordered` signal repeatedly can + * affect the performance of the program, gtk_list_store_insert_with_values() + * should generally be preferred when inserting rows in a sorted list store. + * + * Deprecated: 4.10: Use list models + */ +void +gtk_list_store_insert_with_values (GtkListStore *list_store, + GtkTreeIter *iter, + int position, + ...) +{ + GtkListStorePrivate *priv; + GtkTreePath *path; + GSequence *seq; + GSequenceIter *ptr; + GtkTreeIter tmp_iter; + int length; + gboolean changed = FALSE; + gboolean maybe_need_sort = FALSE; + va_list var_args; + + /* FIXME: refactor to reduce overlap with gtk_list_store_set() */ + g_return_if_fail (GTK_IS_LIST_STORE (list_store)); + + priv = list_store->priv; + + if (!iter) + iter = &tmp_iter; + + priv->columns_dirty = TRUE; + + seq = priv->seq; + + length = g_sequence_get_length (seq); + if (position > length || position < 0) + position = length; + + ptr = g_sequence_get_iter_at_pos (seq, position); + ptr = g_sequence_insert_before (ptr, NULL); + + iter->stamp = priv->stamp; + iter->user_data = ptr; + + g_assert (iter_is_valid (iter, list_store)); + + priv->length++; + + va_start (var_args, position); + gtk_list_store_set_valist_internal (list_store, iter, + &changed, &maybe_need_sort, + var_args); + va_end (var_args); + + /* Don't emit rows_reordered here */ + if (maybe_need_sort && GTK_LIST_STORE_IS_SORTED (list_store)) + g_sequence_sort_changed_iter (iter->user_data, + gtk_list_store_compare_func, + list_store); + + /* Just emit row_inserted */ + path = gtk_list_store_get_path (GTK_TREE_MODEL (list_store), iter); + gtk_tree_model_row_inserted (GTK_TREE_MODEL (list_store), path, iter); + gtk_tree_path_free (path); +} + + +/** + * gtk_list_store_insert_with_valuesv: (rename-to gtk_list_store_insert_with_values) + * @list_store: A `GtkListStore` + * @iter: (out) (optional): An unset `GtkTreeIter` to set to the new row + * @position: position to insert the new row, or -1 for last + * @columns: (array length=n_values): an array of column numbers + * @values: (array length=n_values): an array of GValues + * @n_values: the length of the @columns and @values arrays + * + * A variant of gtk_list_store_insert_with_values() which + * takes the columns and values as two arrays, instead of + * varargs. + * + * This function is mainly intended for language-bindings. + * + * Deprecated: 4.10: Use list models + */ +void +gtk_list_store_insert_with_valuesv (GtkListStore *list_store, + GtkTreeIter *iter, + int position, + int *columns, + GValue *values, + int n_values) +{ + GtkListStorePrivate *priv; + GtkTreePath *path; + GSequence *seq; + GSequenceIter *ptr; + GtkTreeIter tmp_iter; + int length; + gboolean changed = FALSE; + gboolean maybe_need_sort = FALSE; + + /* FIXME refactor to reduce overlap with + * gtk_list_store_insert_with_values() + */ + g_return_if_fail (GTK_IS_LIST_STORE (list_store)); + + priv = list_store->priv; + + if (!iter) + iter = &tmp_iter; + + priv->columns_dirty = TRUE; + + seq = priv->seq; + + length = g_sequence_get_length (seq); + if (position > length || position < 0) + position = length; + + ptr = g_sequence_get_iter_at_pos (seq, position); + ptr = g_sequence_insert_before (ptr, NULL); + + iter->stamp = priv->stamp; + iter->user_data = ptr; + + g_assert (iter_is_valid (iter, list_store)); + + priv->length++; + + gtk_list_store_set_vector_internal (list_store, iter, + &changed, &maybe_need_sort, + columns, values, n_values); + + /* Don't emit rows_reordered here */ + if (maybe_need_sort && GTK_LIST_STORE_IS_SORTED (list_store)) + g_sequence_sort_changed_iter (iter->user_data, + gtk_list_store_compare_func, + list_store); + + /* Just emit row_inserted */ + path = gtk_list_store_get_path (GTK_TREE_MODEL (list_store), iter); + gtk_tree_model_row_inserted (GTK_TREE_MODEL (list_store), path, iter); + gtk_tree_path_free (path); +} + +/* GtkBuildable custom tag implementation + * + * + * + * + * + */ +typedef struct { + gboolean translatable; + char *context; + int id; +} ColInfo; + +typedef struct { + GtkBuilder *builder; + GObject *object; + GSList *column_type_names; + GType *column_types; + GValue *values; + int *colids; + ColInfo **columns; + int last_row; + int n_columns; + int row_column; + gboolean is_data; + const char *domain; +} SubParserData; + +static void +list_store_start_element (GtkBuildableParseContext *context, + const char *element_name, + const char **names, + const char **values, + gpointer user_data, + GError **error) +{ + SubParserData *data = (SubParserData*)user_data; + + if (strcmp (element_name, "col") == 0) + { + int id = -1; + const char *id_str; + const char *msg_context = NULL; + gboolean translatable = FALSE; + ColInfo *info; + GValue val = G_VALUE_INIT; + + if (!_gtk_builder_check_parent (data->builder, context, "row", error)) + return; + + if (data->row_column >= data->n_columns) + { + g_set_error (error, + GTK_BUILDER_ERROR, GTK_BUILDER_ERROR_INVALID_VALUE, + "Too many columns, maximum is %d", data->n_columns - 1); + _gtk_builder_prefix_error (data->builder, context, error); + return; + } + + if (!g_markup_collect_attributes (element_name, names, values, error, + G_MARKUP_COLLECT_STRING, "id", &id_str, + G_MARKUP_COLLECT_BOOLEAN|G_MARKUP_COLLECT_OPTIONAL, "translatable", &translatable, + G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "comments", NULL, + G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "context", &msg_context, + G_MARKUP_COLLECT_INVALID)) + { + _gtk_builder_prefix_error (data->builder, context, error); + return; + } + + if (!gtk_builder_value_from_string_type (data->builder, G_TYPE_INT, id_str, &val, error)) + { + _gtk_builder_prefix_error (data->builder, context, error); + return; + } + + id = g_value_get_int (&val); + if (id < 0 || id >= data->n_columns) + { + g_set_error (error, + GTK_BUILDER_ERROR, GTK_BUILDER_ERROR_INVALID_VALUE, + "id value %d out of range", id); + _gtk_builder_prefix_error (data->builder, context, error); + return; + } + + info = g_slice_new0 (ColInfo); + info->translatable = translatable; + info->context = g_strdup (msg_context); + info->id = id; + + data->colids[data->row_column] = id; + data->columns[data->row_column] = info; + data->row_column++; + data->is_data = TRUE; + } + else if (strcmp (element_name, "row") == 0) + { + if (!_gtk_builder_check_parent (data->builder, context, "data", error)) + return; + + if (!g_markup_collect_attributes (element_name, names, values, error, + G_MARKUP_COLLECT_INVALID, NULL, NULL, + G_MARKUP_COLLECT_INVALID)) + _gtk_builder_prefix_error (data->builder, context, error); + } + else if (strcmp (element_name, "columns") == 0 || + strcmp (element_name, "data") == 0) + { + if (!_gtk_builder_check_parent (data->builder, context, "object", error)) + return; + + if (!g_markup_collect_attributes (element_name, names, values, error, + G_MARKUP_COLLECT_INVALID, NULL, NULL, + G_MARKUP_COLLECT_INVALID)) + _gtk_builder_prefix_error (data->builder, context, error); + } + else if (strcmp (element_name, "column") == 0) + { + const char *type; + + if (!_gtk_builder_check_parent (data->builder, context, "columns", error)) + return; + + if (!g_markup_collect_attributes (element_name, names, values, error, + G_MARKUP_COLLECT_STRING, "type", &type, + G_MARKUP_COLLECT_INVALID)) + { + _gtk_builder_prefix_error (data->builder, context, error); + return; + } + + data->column_type_names = g_slist_prepend (data->column_type_names, g_strdup (type)); + } + else + { + _gtk_builder_error_unhandled_tag (data->builder, context, + "GtkListStore", element_name, + error); + } +} + +static void +list_store_end_element (GtkBuildableParseContext *context, + const char *element_name, + gpointer user_data, + GError **error) +{ + SubParserData *data = (SubParserData*)user_data; + + g_assert (data->builder); + + if (strcmp (element_name, "row") == 0) + { + GtkTreeIter iter; + int i; + + gtk_list_store_insert_with_valuesv (GTK_LIST_STORE (data->object), + &iter, + data->last_row, + data->colids, + data->values, + data->row_column); + for (i = 0; i < data->row_column; i++) + { + ColInfo *info = data->columns[i]; + g_free (info->context); + g_slice_free (ColInfo, info); + data->columns[i] = NULL; + g_value_unset (&data->values[i]); + } + g_free (data->values); + data->values = g_new0 (GValue, data->n_columns); + data->last_row++; + data->row_column = 0; + } + else if (strcmp (element_name, "columns") == 0) + { + GType *column_types; + GSList *l; + int i; + GType type; + + data->column_type_names = g_slist_reverse (data->column_type_names); + column_types = g_new0 (GType, g_slist_length (data->column_type_names)); + + for (l = data->column_type_names, i = 0; l; l = l->next, i++) + { + type = gtk_builder_get_type_from_name (data->builder, l->data); + if (type == G_TYPE_INVALID) + { + g_warning ("Unknown type %s specified in treemodel %s", + (const char *)l->data, + gtk_buildable_get_buildable_id (GTK_BUILDABLE (data->object))); + continue; + } + column_types[i] = type; + + g_free (l->data); + } + + gtk_list_store_set_column_types (GTK_LIST_STORE (data->object), i, column_types); + + g_free (column_types); + } + else if (strcmp (element_name, "col") == 0) + { + data->is_data = FALSE; + } +} + +static void +list_store_text (GtkBuildableParseContext *context, + const char *text, + gsize text_len, + gpointer user_data, + GError **error) +{ + SubParserData *data = (SubParserData*)user_data; + int i; + char *string; + ColInfo *info; + + if (!data->is_data) + return; + + i = data->row_column - 1; + info = data->columns[i]; + + string = g_strndup (text, text_len); + if (info->translatable && text_len) + { + char *translated; + + /* FIXME: This will not use the domain set in the .ui file, + * since the parser is not telling the builder about the domain. + * However, it will work for gtk_builder_set_translation_domain() calls. + */ + translated = g_strdup (_gtk_builder_parser_translate (data->domain, + info->context, + string)); + g_free (string); + string = translated; + } + + if (!gtk_builder_value_from_string_type (data->builder, + data->column_types[info->id], + string, + &data->values[i], + error)) + { + _gtk_builder_prefix_error (data->builder, context, error); + } + g_free (string); +} + +static const GtkBuildableParser list_store_parser = + { + list_store_start_element, + list_store_end_element, + list_store_text + }; + +static gboolean +gtk_list_store_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const char *tagname, + GtkBuildableParser *parser, + gpointer *parser_data) +{ + SubParserData *data; + + if (child) + return FALSE; + + if (strcmp (tagname, "columns") == 0) + { + data = g_slice_new0 (SubParserData); + data->builder = builder; + data->object = G_OBJECT (buildable); + data->column_type_names = NULL; + + *parser = list_store_parser; + *parser_data = data; + + return TRUE; + } + else if (strcmp (tagname, "data") == 0) + { + int n_columns = gtk_list_store_get_n_columns (GTK_TREE_MODEL (buildable)); + if (n_columns == 0) + g_error ("Cannot append data to an empty model"); + + data = g_slice_new0 (SubParserData); + data->builder = builder; + data->object = G_OBJECT (buildable); + data->values = g_new0 (GValue, n_columns); + data->colids = g_new0 (int, n_columns); + data->columns = g_new0 (ColInfo*, n_columns); + data->column_types = GTK_LIST_STORE (buildable)->priv->column_headers; + data->n_columns = n_columns; + data->last_row = 0; + data->domain = gtk_builder_get_translation_domain (builder); + + *parser = list_store_parser; + *parser_data = data; + + return TRUE; + } + + return FALSE; +} + +static void +gtk_list_store_buildable_custom_tag_end (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const char *tagname, + gpointer parser_data) +{ + SubParserData *data = (SubParserData*)parser_data; + + if (strcmp (tagname, "columns") == 0) + { + g_slist_free (data->column_type_names); + g_slice_free (SubParserData, data); + } + else if (strcmp (tagname, "data") == 0) + { + int i; + for (i = 0; i < data->n_columns; i++) + { + ColInfo *info = data->columns[i]; + if (info) + { + g_free (info->context); + g_slice_free (ColInfo, info); + } + } + g_free (data->colids); + g_free (data->columns); + g_free (data->values); + g_slice_free (SubParserData, data); + } +} diff --git a/gtk/deprecated/gtkliststore.h b/gtk/deprecated/gtkliststore.h new file mode 100644 index 0000000000..75914bfe8a --- /dev/null +++ b/gtk/deprecated/gtkliststore.h @@ -0,0 +1,154 @@ +/* gtkliststore.h + * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#ifndef __GTK_LIST_STORE_H__ +#define __GTK_LIST_STORE_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include +#include + + +G_BEGIN_DECLS + + +#define GTK_TYPE_LIST_STORE (gtk_list_store_get_type ()) +#define GTK_LIST_STORE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_LIST_STORE, GtkListStore)) +#define GTK_LIST_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_LIST_STORE, GtkListStoreClass)) +#define GTK_IS_LIST_STORE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_LIST_STORE)) +#define GTK_IS_LIST_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_LIST_STORE)) +#define GTK_LIST_STORE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_LIST_STORE, GtkListStoreClass)) + +typedef struct _GtkListStore GtkListStore; +typedef struct _GtkListStorePrivate GtkListStorePrivate; +typedef struct _GtkListStoreClass GtkListStoreClass; + +struct _GtkListStore +{ + GObject parent; + + /*< private >*/ + GtkListStorePrivate *priv; +}; + +struct _GtkListStoreClass +{ + GObjectClass parent_class; + + /*< private >*/ + gpointer padding[8]; +}; + + +GDK_AVAILABLE_IN_ALL +GType gtk_list_store_get_type (void) G_GNUC_CONST; +GDK_DEPRECATED_IN_4_10 +GtkListStore *gtk_list_store_new (int n_columns, + ...); +GDK_DEPRECATED_IN_4_10 +GtkListStore *gtk_list_store_newv (int n_columns, + GType *types); +GDK_DEPRECATED_IN_4_10 +void gtk_list_store_set_column_types (GtkListStore *list_store, + int n_columns, + GType *types); + +/* NOTE: use gtk_tree_model_get to get values from a GtkListStore */ + +GDK_DEPRECATED_IN_4_10 +void gtk_list_store_set_value (GtkListStore *list_store, + GtkTreeIter *iter, + int column, + GValue *value); +GDK_DEPRECATED_IN_4_10 +void gtk_list_store_set (GtkListStore *list_store, + GtkTreeIter *iter, + ...); +GDK_DEPRECATED_IN_4_10 +void gtk_list_store_set_valuesv (GtkListStore *list_store, + GtkTreeIter *iter, + int *columns, + GValue *values, + int n_values); +GDK_DEPRECATED_IN_4_10 +void gtk_list_store_set_valist (GtkListStore *list_store, + GtkTreeIter *iter, + va_list var_args); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_list_store_remove (GtkListStore *list_store, + GtkTreeIter *iter); +GDK_DEPRECATED_IN_4_10 +void gtk_list_store_insert (GtkListStore *list_store, + GtkTreeIter *iter, + int position); +GDK_DEPRECATED_IN_4_10 +void gtk_list_store_insert_before (GtkListStore *list_store, + GtkTreeIter *iter, + GtkTreeIter *sibling); +GDK_DEPRECATED_IN_4_10 +void gtk_list_store_insert_after (GtkListStore *list_store, + GtkTreeIter *iter, + GtkTreeIter *sibling); +GDK_DEPRECATED_IN_4_10 +void gtk_list_store_insert_with_values (GtkListStore *list_store, + GtkTreeIter *iter, + int position, + ...); +GDK_DEPRECATED_IN_4_10 +void gtk_list_store_insert_with_valuesv (GtkListStore *list_store, + GtkTreeIter *iter, + int position, + int *columns, + GValue *values, + int n_values); +GDK_DEPRECATED_IN_4_10 +void gtk_list_store_prepend (GtkListStore *list_store, + GtkTreeIter *iter); +GDK_DEPRECATED_IN_4_10 +void gtk_list_store_append (GtkListStore *list_store, + GtkTreeIter *iter); +GDK_DEPRECATED_IN_4_10 +void gtk_list_store_clear (GtkListStore *list_store); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_list_store_iter_is_valid (GtkListStore *list_store, + GtkTreeIter *iter); +GDK_DEPRECATED_IN_4_10 +void gtk_list_store_reorder (GtkListStore *store, + int *new_order); +GDK_DEPRECATED_IN_4_10 +void gtk_list_store_swap (GtkListStore *store, + GtkTreeIter *a, + GtkTreeIter *b); +GDK_DEPRECATED_IN_4_10 +void gtk_list_store_move_after (GtkListStore *store, + GtkTreeIter *iter, + GtkTreeIter *position); +GDK_DEPRECATED_IN_4_10 +void gtk_list_store_move_before (GtkListStore *store, + GtkTreeIter *iter, + GtkTreeIter *position); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkListStore, g_object_unref) + +G_END_DECLS + + +#endif /* __GTK_LIST_STORE_H__ */ diff --git a/gtk/deprecated/gtktreedatalist.c b/gtk/deprecated/gtktreedatalist.c new file mode 100644 index 0000000000..5a0e212e29 --- /dev/null +++ b/gtk/deprecated/gtktreedatalist.c @@ -0,0 +1,574 @@ +/* gtktreedatalist.c + * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + * + * This file contains code shared between GtkTreeStore and GtkListStore. Please + * do not use it. + */ + +#include "config.h" +#include "gtktreedatalistprivate.h" +#include + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + +/* node allocation + */ +GtkTreeDataList * +_gtk_tree_data_list_alloc (void) +{ + GtkTreeDataList *list; + + list = g_slice_new0 (GtkTreeDataList); + + return list; +} + +void +_gtk_tree_data_list_free (GtkTreeDataList *list, + GType *column_headers) +{ + GtkTreeDataList *tmp, *next; + int i = 0; + + tmp = list; + + while (tmp) + { + next = tmp->next; + if (g_type_is_a (column_headers [i], G_TYPE_STRING)) + g_free ((char *) tmp->data.v_pointer); + else if (g_type_is_a (column_headers [i], G_TYPE_OBJECT) && tmp->data.v_pointer != NULL) + g_object_unref (tmp->data.v_pointer); + else if (g_type_is_a (column_headers [i], G_TYPE_BOXED) && tmp->data.v_pointer != NULL) + g_boxed_free (column_headers [i], (gpointer) tmp->data.v_pointer); + else if (g_type_is_a (column_headers [i], G_TYPE_VARIANT) && tmp->data.v_pointer != NULL) + g_variant_unref ((gpointer) tmp->data.v_pointer); + + g_slice_free (GtkTreeDataList, tmp); + i++; + tmp = next; + } +} + +gboolean +_gtk_tree_data_list_check_type (GType type) +{ + int i = 0; + static const GType type_list[] = + { + G_TYPE_BOOLEAN, + G_TYPE_CHAR, + G_TYPE_UCHAR, + G_TYPE_INT, + G_TYPE_UINT, + G_TYPE_LONG, + G_TYPE_ULONG, + G_TYPE_INT64, + G_TYPE_UINT64, + G_TYPE_ENUM, + G_TYPE_FLAGS, + G_TYPE_FLOAT, + G_TYPE_DOUBLE, + G_TYPE_STRING, + G_TYPE_POINTER, + G_TYPE_BOXED, + G_TYPE_OBJECT, + G_TYPE_VARIANT, + G_TYPE_INVALID + }; + + if (! G_TYPE_IS_VALUE_TYPE (type)) + return FALSE; + + + while (type_list[i] != G_TYPE_INVALID) + { + if (g_type_is_a (type, type_list[i])) + return TRUE; + i++; + } + return FALSE; +} + +static inline GType +get_fundamental_type (GType type) +{ + GType result; + + result = G_TYPE_FUNDAMENTAL (type); + + if (result == G_TYPE_INTERFACE) + { + if (g_type_is_a (type, G_TYPE_OBJECT)) + result = G_TYPE_OBJECT; + } + + return result; +} +void +_gtk_tree_data_list_node_to_value (GtkTreeDataList *list, + GType type, + GValue *value) +{ + g_value_init (value, type); + + switch (get_fundamental_type (type)) + { + case G_TYPE_BOOLEAN: + g_value_set_boolean (value, (gboolean) list->data.v_int); + break; + case G_TYPE_CHAR: + g_value_set_schar (value, (char) list->data.v_char); + break; + case G_TYPE_UCHAR: + g_value_set_uchar (value, (guchar) list->data.v_uchar); + break; + case G_TYPE_INT: + g_value_set_int (value, (int) list->data.v_int); + break; + case G_TYPE_UINT: + g_value_set_uint (value, (guint) list->data.v_uint); + break; + case G_TYPE_LONG: + g_value_set_long (value, list->data.v_long); + break; + case G_TYPE_ULONG: + g_value_set_ulong (value, list->data.v_ulong); + break; + case G_TYPE_INT64: + g_value_set_int64 (value, list->data.v_int64); + break; + case G_TYPE_UINT64: + g_value_set_uint64 (value, list->data.v_uint64); + break; + case G_TYPE_ENUM: + g_value_set_enum (value, list->data.v_int); + break; + case G_TYPE_FLAGS: + g_value_set_flags (value, list->data.v_uint); + break; + case G_TYPE_FLOAT: + g_value_set_float (value, (float) list->data.v_float); + break; + case G_TYPE_DOUBLE: + g_value_set_double (value, (double) list->data.v_double); + break; + case G_TYPE_STRING: + g_value_set_string (value, (char *) list->data.v_pointer); + break; + case G_TYPE_POINTER: + g_value_set_pointer (value, (gpointer) list->data.v_pointer); + break; + case G_TYPE_BOXED: + g_value_set_boxed (value, (gpointer) list->data.v_pointer); + break; + case G_TYPE_VARIANT: + g_value_set_variant (value, (gpointer) list->data.v_pointer); + break; + case G_TYPE_OBJECT: + g_value_set_object (value, (GObject *) list->data.v_pointer); + break; + default: + g_warning ("%s: Unsupported type (%s) retrieved.", G_STRLOC, g_type_name (value->g_type)); + break; + } +} + +void +_gtk_tree_data_list_value_to_node (GtkTreeDataList *list, + GValue *value) +{ + switch (get_fundamental_type (G_VALUE_TYPE (value))) + { + case G_TYPE_BOOLEAN: + list->data.v_int = g_value_get_boolean (value); + break; + case G_TYPE_CHAR: + list->data.v_char = g_value_get_schar (value); + break; + case G_TYPE_UCHAR: + list->data.v_uchar = g_value_get_uchar (value); + break; + case G_TYPE_INT: + list->data.v_int = g_value_get_int (value); + break; + case G_TYPE_UINT: + list->data.v_uint = g_value_get_uint (value); + break; + case G_TYPE_LONG: + list->data.v_long = g_value_get_long (value); + break; + case G_TYPE_ULONG: + list->data.v_ulong = g_value_get_ulong (value); + break; + case G_TYPE_INT64: + list->data.v_int64 = g_value_get_int64 (value); + break; + case G_TYPE_UINT64: + list->data.v_uint64 = g_value_get_uint64 (value); + break; + case G_TYPE_ENUM: + list->data.v_int = g_value_get_enum (value); + break; + case G_TYPE_FLAGS: + list->data.v_uint = g_value_get_flags (value); + break; + case G_TYPE_POINTER: + list->data.v_pointer = g_value_get_pointer (value); + break; + case G_TYPE_FLOAT: + list->data.v_float = g_value_get_float (value); + break; + case G_TYPE_DOUBLE: + list->data.v_double = g_value_get_double (value); + break; + case G_TYPE_STRING: + g_free (list->data.v_pointer); + list->data.v_pointer = g_value_dup_string (value); + break; + case G_TYPE_OBJECT: + if (list->data.v_pointer) + g_object_unref (list->data.v_pointer); + list->data.v_pointer = g_value_dup_object (value); + break; + case G_TYPE_BOXED: + if (list->data.v_pointer) + g_boxed_free (G_VALUE_TYPE (value), list->data.v_pointer); + list->data.v_pointer = g_value_dup_boxed (value); + break; + case G_TYPE_VARIANT: + if (list->data.v_pointer) + g_variant_unref (list->data.v_pointer); + list->data.v_pointer = g_value_dup_variant (value); + break; + default: + g_warning ("%s: Unsupported type (%s) stored.", G_STRLOC, g_type_name (G_VALUE_TYPE (value))); + break; + } +} + +GtkTreeDataList * +_gtk_tree_data_list_node_copy (GtkTreeDataList *list, + GType type) +{ + GtkTreeDataList *new_list; + + g_return_val_if_fail (list != NULL, NULL); + + new_list = _gtk_tree_data_list_alloc (); + new_list->next = NULL; + + switch (get_fundamental_type (type)) + { + case G_TYPE_BOOLEAN: + case G_TYPE_CHAR: + case G_TYPE_UCHAR: + case G_TYPE_INT: + case G_TYPE_UINT: + case G_TYPE_LONG: + case G_TYPE_ULONG: + case G_TYPE_INT64: + case G_TYPE_UINT64: + case G_TYPE_ENUM: + case G_TYPE_FLAGS: + case G_TYPE_POINTER: + case G_TYPE_FLOAT: + case G_TYPE_DOUBLE: + new_list->data = list->data; + break; + case G_TYPE_STRING: + new_list->data.v_pointer = g_strdup (list->data.v_pointer); + break; + case G_TYPE_OBJECT: + case G_TYPE_INTERFACE: + new_list->data.v_pointer = list->data.v_pointer; + if (new_list->data.v_pointer) + g_object_ref (new_list->data.v_pointer); + break; + case G_TYPE_BOXED: + if (list->data.v_pointer) + new_list->data.v_pointer = g_boxed_copy (type, list->data.v_pointer); + else + new_list->data.v_pointer = NULL; + break; + case G_TYPE_VARIANT: + if (list->data.v_pointer) + new_list->data.v_pointer = g_variant_ref (list->data.v_pointer); + else + new_list->data.v_pointer = NULL; + break; + default: + g_warning ("Unsupported node type (%s) copied.", g_type_name (type)); + break; + } + + return new_list; +} + +int +_gtk_tree_data_list_compare_func (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data) +{ + int column = GPOINTER_TO_INT (user_data); + GType type = gtk_tree_model_get_column_type (model, column); + GValue a_value = G_VALUE_INIT; + GValue b_value = G_VALUE_INIT; + int retval; + const char *stra, *strb; + + gtk_tree_model_get_value (model, a, column, &a_value); + gtk_tree_model_get_value (model, b, column, &b_value); + + switch (get_fundamental_type (type)) + { + case G_TYPE_BOOLEAN: + if (g_value_get_boolean (&a_value) < g_value_get_boolean (&b_value)) + retval = -1; + else if (g_value_get_boolean (&a_value) == g_value_get_boolean (&b_value)) + retval = 0; + else + retval = 1; + break; + case G_TYPE_CHAR: + if (g_value_get_schar (&a_value) < g_value_get_schar (&b_value)) + retval = -1; + else if (g_value_get_schar (&a_value) == g_value_get_schar (&b_value)) + retval = 0; + else + retval = 1; + break; + case G_TYPE_UCHAR: + if (g_value_get_uchar (&a_value) < g_value_get_uchar (&b_value)) + retval = -1; + else if (g_value_get_uchar (&a_value) == g_value_get_uchar (&b_value)) + retval = 0; + else + retval = 1; + break; + case G_TYPE_INT: + if (g_value_get_int (&a_value) < g_value_get_int (&b_value)) + retval = -1; + else if (g_value_get_int (&a_value) == g_value_get_int (&b_value)) + retval = 0; + else + retval = 1; + break; + case G_TYPE_UINT: + if (g_value_get_uint (&a_value) < g_value_get_uint (&b_value)) + retval = -1; + else if (g_value_get_uint (&a_value) == g_value_get_uint (&b_value)) + retval = 0; + else + retval = 1; + break; + case G_TYPE_LONG: + if (g_value_get_long (&a_value) < g_value_get_long (&b_value)) + retval = -1; + else if (g_value_get_long (&a_value) == g_value_get_long (&b_value)) + retval = 0; + else + retval = 1; + break; + case G_TYPE_ULONG: + if (g_value_get_ulong (&a_value) < g_value_get_ulong (&b_value)) + retval = -1; + else if (g_value_get_ulong (&a_value) == g_value_get_ulong (&b_value)) + retval = 0; + else + retval = 1; + break; + case G_TYPE_INT64: + if (g_value_get_int64 (&a_value) < g_value_get_int64 (&b_value)) + retval = -1; + else if (g_value_get_int64 (&a_value) == g_value_get_int64 (&b_value)) + retval = 0; + else + retval = 1; + break; + case G_TYPE_UINT64: + if (g_value_get_uint64 (&a_value) < g_value_get_uint64 (&b_value)) + retval = -1; + else if (g_value_get_uint64 (&a_value) == g_value_get_uint64 (&b_value)) + retval = 0; + else + retval = 1; + break; + case G_TYPE_ENUM: + /* this is somewhat bogus. */ + if (g_value_get_enum (&a_value) < g_value_get_enum (&b_value)) + retval = -1; + else if (g_value_get_enum (&a_value) == g_value_get_enum (&b_value)) + retval = 0; + else + retval = 1; + break; + case G_TYPE_FLAGS: + /* this is even more bogus. */ + if (g_value_get_flags (&a_value) < g_value_get_flags (&b_value)) + retval = -1; + else if (g_value_get_flags (&a_value) == g_value_get_flags (&b_value)) + retval = 0; + else + retval = 1; + break; + case G_TYPE_FLOAT: + if (g_value_get_float (&a_value) < g_value_get_float (&b_value)) + retval = -1; + else if (g_value_get_float (&a_value) == g_value_get_float (&b_value)) + retval = 0; + else + retval = 1; + break; + case G_TYPE_DOUBLE: + if (g_value_get_double (&a_value) < g_value_get_double (&b_value)) + retval = -1; + else if (g_value_get_double (&a_value) == g_value_get_double (&b_value)) + retval = 0; + else + retval = 1; + break; + case G_TYPE_STRING: + stra = g_value_get_string (&a_value); + strb = g_value_get_string (&b_value); + if (stra == NULL) stra = ""; + if (strb == NULL) strb = ""; + retval = g_utf8_collate (stra, strb); + break; + case G_TYPE_VARIANT: + case G_TYPE_POINTER: + case G_TYPE_BOXED: + case G_TYPE_OBJECT: + default: + g_warning ("Attempting to sort on invalid type %s", g_type_name (type)); + retval = FALSE; + break; + } + + g_value_unset (&a_value); + g_value_unset (&b_value); + + return retval; +} + + +GList * +_gtk_tree_data_list_header_new (int n_columns, + GType *types) +{ + GList *retval = NULL; + + int i; + + for (i = 0; i < n_columns; i ++) + { + GtkTreeDataSortHeader *header; + + header = g_slice_new (GtkTreeDataSortHeader); + + retval = g_list_prepend (retval, header); + header->sort_column_id = i; + header->func = _gtk_tree_data_list_compare_func; + header->destroy = NULL; + header->data = GINT_TO_POINTER (i); + } + return g_list_reverse (retval); +} + +void +_gtk_tree_data_list_header_free (GList *list) +{ + GList *tmp; + + for (tmp = list; tmp; tmp = tmp->next) + { + GtkTreeDataSortHeader *header = (GtkTreeDataSortHeader *) tmp->data; + + if (header->destroy) + { + GDestroyNotify d = header->destroy; + + header->destroy = NULL; + d (header->data); + } + + g_slice_free (GtkTreeDataSortHeader, header); + } + g_list_free (list); +} + +GtkTreeDataSortHeader * +_gtk_tree_data_list_get_header (GList *header_list, + int sort_column_id) +{ + GtkTreeDataSortHeader *header = NULL; + + for (; header_list; header_list = header_list->next) + { + header = (GtkTreeDataSortHeader*) header_list->data; + if (header->sort_column_id == sort_column_id) + return header; + } + return NULL; +} + + +GList * +_gtk_tree_data_list_set_header (GList *header_list, + int sort_column_id, + GtkTreeIterCompareFunc func, + gpointer data, + GDestroyNotify destroy) +{ + GList *list = header_list; + GtkTreeDataSortHeader *header = NULL; + + for (; list; list = list->next) + { + header = (GtkTreeDataSortHeader*) list->data; + if (header->sort_column_id == sort_column_id) + break; + header = NULL; + + if (list->next == NULL) + break; + } + + if (header == NULL) + { + header = g_slice_new0 (GtkTreeDataSortHeader); + header->sort_column_id = sort_column_id; + if (list) + list = g_list_append (list, header); + else + header_list = g_list_append (header_list, header); + } + + if (header->destroy) + { + GDestroyNotify d = header->destroy; + + header->destroy = NULL; + d (header->data); + } + + header->func = func; + header->data = data; + header->destroy = destroy; + + return header_list; +} diff --git a/gtk/deprecated/gtktreedatalistprivate.h b/gtk/deprecated/gtktreedatalistprivate.h new file mode 100644 index 0000000000..a0373b8bcd --- /dev/null +++ b/gtk/deprecated/gtktreedatalistprivate.h @@ -0,0 +1,81 @@ +/* gtktreedatalist.h + * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#ifndef __GTK_TREE_DATA_LIST_PRIVATE_H__ +#define __GTK_TREE_DATA_LIST_PRIVATE_H__ + +#include +#include + +typedef struct _GtkTreeDataList GtkTreeDataList; +struct _GtkTreeDataList +{ + GtkTreeDataList *next; + + union { + int v_int; + gint8 v_char; + guint8 v_uchar; + guint v_uint; + glong v_long; + gulong v_ulong; + gint64 v_int64; + guint64 v_uint64; + float v_float; + double v_double; + gpointer v_pointer; + } data; +}; + +typedef struct _GtkTreeDataSortHeader +{ + int sort_column_id; + GtkTreeIterCompareFunc func; + gpointer data; + GDestroyNotify destroy; +} GtkTreeDataSortHeader; + +GtkTreeDataList *_gtk_tree_data_list_alloc (void); +void _gtk_tree_data_list_free (GtkTreeDataList *list, + GType *column_headers); +gboolean _gtk_tree_data_list_check_type (GType type); +void _gtk_tree_data_list_node_to_value (GtkTreeDataList *list, + GType type, + GValue *value); +void _gtk_tree_data_list_value_to_node (GtkTreeDataList *list, + GValue *value); + +GtkTreeDataList *_gtk_tree_data_list_node_copy (GtkTreeDataList *list, + GType type); + +/* Header code */ +int _gtk_tree_data_list_compare_func (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data); +GList * _gtk_tree_data_list_header_new (int n_columns, + GType *types); +void _gtk_tree_data_list_header_free (GList *header_list); +GtkTreeDataSortHeader *_gtk_tree_data_list_get_header (GList *header_list, + int sort_column_id); +GList *_gtk_tree_data_list_set_header (GList *header_list, + int sort_column_id, + GtkTreeIterCompareFunc func, + gpointer data, + GDestroyNotify destroy); + +#endif /* __GTK_TREE_DATA_LIST_PRIVATE_H__ */ diff --git a/gtk/deprecated/gtktreednd.c b/gtk/deprecated/gtktreednd.c new file mode 100644 index 0000000000..d7dc232fab --- /dev/null +++ b/gtk/deprecated/gtktreednd.c @@ -0,0 +1,372 @@ +/* gtktreednd.c + * Copyright (C) 2001 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#include "config.h" +#include +#include "gtktreednd.h" + +#include "gtkprivate.h" + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + +/** + * SECTION:gtktreednd + * @Short_description: Interfaces for drag-and-drop support in GtkTreeView + * @Title: GtkTreeView drag-and-drop + * + * GTK supports Drag-and-Drop in tree views with a high-level and a low-level + * API. + * + * The low-level API consists of the GTK DND API, augmented by some treeview + * utility functions: gtk_tree_view_set_drag_dest_row(), + * gtk_tree_view_get_drag_dest_row(), gtk_tree_view_get_dest_row_at_pos(), + * gtk_tree_view_create_row_drag_icon(), gtk_tree_set_row_drag_data() and + * gtk_tree_get_row_drag_data(). This API leaves a lot of flexibility, but + * nothing is done automatically, and implementing advanced features like + * hover-to-open-rows or autoscrolling on top of this API is a lot of work. + * + * On the other hand, if you write to the high-level API, then all the + * bookkeeping of rows is done for you, as well as things like hover-to-open + * and auto-scroll, but your models have to implement the + * `GtkTreeDragSource` and `GtkTreeDragDest` interfaces. + */ + +/** + * GtkTreeDragDest: + * + * Interface for Drag-and-Drop destinations in `GtkTreeView`. + */ + +/** + * GtkTreeDragSource: + * + * Interface for Drag-and-Drop destinations in `GtkTreeView`. + */ + +GType +gtk_tree_drag_source_get_type (void) +{ + static GType our_type = 0; + + if (!our_type) + { + const GTypeInfo our_info = + { + sizeof (GtkTreeDragSourceIface), /* class_size */ + NULL, /* base_init */ + NULL, /* base_finalize */ + NULL, + NULL, /* class_finalize */ + NULL, /* class_data */ + 0, + 0, /* n_preallocs */ + NULL + }; + + our_type = g_type_register_static (G_TYPE_INTERFACE, + I_("GtkTreeDragSource"), + &our_info, 0); + } + + return our_type; +} + + +GType +gtk_tree_drag_dest_get_type (void) +{ + static GType our_type = 0; + + if (!our_type) + { + const GTypeInfo our_info = + { + sizeof (GtkTreeDragDestIface), /* class_size */ + NULL, /* base_init */ + NULL, /* base_finalize */ + NULL, + NULL, /* class_finalize */ + NULL, /* class_data */ + 0, + 0, /* n_preallocs */ + NULL + }; + + our_type = g_type_register_static (G_TYPE_INTERFACE, I_("GtkTreeDragDest"), &our_info, 0); + } + + return our_type; +} + +/** + * gtk_tree_drag_source_row_draggable: + * @drag_source: a `GtkTreeDragSource` + * @path: row on which user is initiating a drag + * + * Asks the `GtkTreeDragSource` whether a particular row can be used as + * the source of a DND operation. If the source doesn’t implement + * this interface, the row is assumed draggable. + * + * Returns: %TRUE if the row can be dragged + * + * Deprecated: 4.10: Use list models instead + **/ +gboolean +gtk_tree_drag_source_row_draggable (GtkTreeDragSource *drag_source, + GtkTreePath *path) +{ + GtkTreeDragSourceIface *iface = GTK_TREE_DRAG_SOURCE_GET_IFACE (drag_source); + + g_return_val_if_fail (path != NULL, FALSE); + + if (iface->row_draggable) + return (* iface->row_draggable) (drag_source, path); + else + return TRUE; + /* Returning TRUE if row_draggable is not implemented is a fallback. + Interface implementations such as GtkTreeStore and GtkListStore really should + implement row_draggable. */ +} + + +/** + * gtk_tree_drag_source_drag_data_delete: + * @drag_source: a `GtkTreeDragSource` + * @path: row that was being dragged + * + * Asks the `GtkTreeDragSource` to delete the row at @path, because + * it was moved somewhere else via drag-and-drop. Returns %FALSE + * if the deletion fails because @path no longer exists, or for + * some model-specific reason. Should robustly handle a @path no + * longer found in the model! + * + * Returns: %TRUE if the row was successfully deleted + * + * Deprecated: 4.10: Use list models instead + **/ +gboolean +gtk_tree_drag_source_drag_data_delete (GtkTreeDragSource *drag_source, + GtkTreePath *path) +{ + GtkTreeDragSourceIface *iface = GTK_TREE_DRAG_SOURCE_GET_IFACE (drag_source); + + g_return_val_if_fail (iface->drag_data_delete != NULL, FALSE); + g_return_val_if_fail (path != NULL, FALSE); + + return (* iface->drag_data_delete) (drag_source, path); +} + +/** + * gtk_tree_drag_source_drag_data_get: + * @drag_source: a `GtkTreeDragSource` + * @path: row that was dragged + * + * Asks the `GtkTreeDragSource` to return a `GdkContentProvider` representing + * the row at @path. Should robustly handle a @path no + * longer found in the model! + * + * Returns: (nullable) (transfer full): a `GdkContentProvider` for the + * given @path + * + * Deprecated: 4.10: Use list models instead + **/ +GdkContentProvider * +gtk_tree_drag_source_drag_data_get (GtkTreeDragSource *drag_source, + GtkTreePath *path) +{ + GtkTreeDragSourceIface *iface = GTK_TREE_DRAG_SOURCE_GET_IFACE (drag_source); + + g_return_val_if_fail (iface->drag_data_get != NULL, FALSE); + g_return_val_if_fail (path != NULL, FALSE); + + return (* iface->drag_data_get) (drag_source, path); +} + +/** + * gtk_tree_drag_dest_drag_data_received: + * @drag_dest: a `GtkTreeDragDest` + * @dest: row to drop in front of + * @value: data to drop + * + * Asks the `GtkTreeDragDest` to insert a row before the path @dest, + * deriving the contents of the row from @value. If @dest is + * outside the tree so that inserting before it is impossible, %FALSE + * will be returned. Also, %FALSE may be returned if the new row is + * not created for some model-specific reason. Should robustly handle + * a @dest no longer found in the model! + * + * Returns: whether a new row was created before position @dest + * + * Deprecated: 4.10: Use list models instead + **/ +gboolean +gtk_tree_drag_dest_drag_data_received (GtkTreeDragDest *drag_dest, + GtkTreePath *dest, + const GValue *value) +{ + GtkTreeDragDestIface *iface = GTK_TREE_DRAG_DEST_GET_IFACE (drag_dest); + + g_return_val_if_fail (iface->drag_data_received != NULL, FALSE); + g_return_val_if_fail (dest != NULL, FALSE); + g_return_val_if_fail (value != NULL, FALSE); + + return (* iface->drag_data_received) (drag_dest, dest, value); +} + + +/** + * gtk_tree_drag_dest_row_drop_possible: + * @drag_dest: a `GtkTreeDragDest` + * @dest_path: destination row + * @value: the data being dropped + * + * Determines whether a drop is possible before the given @dest_path, + * at the same depth as @dest_path. i.e., can we drop the data in + * @value at that location. @dest_path does not have to + * exist; the return value will almost certainly be %FALSE if the + * parent of @dest_path doesn’t exist, though. + * + * Returns: %TRUE if a drop is possible before @dest_path + * + * Deprecated: 4.10: Use list models instead + **/ +gboolean +gtk_tree_drag_dest_row_drop_possible (GtkTreeDragDest *drag_dest, + GtkTreePath *dest_path, + const GValue *value) +{ + GtkTreeDragDestIface *iface = GTK_TREE_DRAG_DEST_GET_IFACE (drag_dest); + + g_return_val_if_fail (iface->row_drop_possible != NULL, FALSE); + g_return_val_if_fail (dest_path != NULL, FALSE); + g_return_val_if_fail (value != NULL, FALSE); + + return (* iface->row_drop_possible) (drag_dest, dest_path, value); +} + +typedef struct _GtkTreeRowData GtkTreeRowData; + +struct _GtkTreeRowData +{ + GtkTreeModel *model; + char path[4]; +}; + +static GtkTreeRowData * +gtk_tree_row_data_copy (GtkTreeRowData *src) +{ + return g_memdup2 (src, sizeof (GtkTreeRowData) + strlen (src->path) + 1 - + (sizeof (GtkTreeRowData) - G_STRUCT_OFFSET (GtkTreeRowData, path))); +} + +G_DEFINE_BOXED_TYPE (GtkTreeRowData, gtk_tree_row_data, + gtk_tree_row_data_copy, + g_free) + +/** + * gtk_tree_create_row_drag_content: + * @tree_model: a `GtkTreeModel` + * @path: a row in @tree_model + * + * Creates a content provider for dragging @path from @tree_model. + * + * Returns: (transfer full): a new `GdkContentProvider` + * + * Deprecated: 4.10: Use list models instead + */ +GdkContentProvider * +gtk_tree_create_row_drag_content (GtkTreeModel *tree_model, + GtkTreePath *path) +{ + GdkContentProvider *content; + GtkTreeRowData *trd; + char *path_str; + int len; + int struct_size; + + g_return_val_if_fail (GTK_IS_TREE_MODEL (tree_model), FALSE); + g_return_val_if_fail (path != NULL, FALSE); + + path_str = gtk_tree_path_to_string (path); + + len = strlen (path_str); + + /* the old allocate-end-of-struct-to-hold-string trick */ + struct_size = sizeof (GtkTreeRowData) + len + 1 - + (sizeof (GtkTreeRowData) - G_STRUCT_OFFSET (GtkTreeRowData, path)); + + trd = g_malloc (struct_size); + + strcpy (trd->path, path_str); + + g_free (path_str); + + trd->model = tree_model; + + content = gdk_content_provider_new_typed (GTK_TYPE_TREE_ROW_DATA, trd); + + g_free (trd); + + return content; +} + +/** + * gtk_tree_get_row_drag_data: + * @value: a `GValue` + * @tree_model: (nullable) (optional) (transfer none) (out): a `GtkTreeModel` + * @path: (nullable) (optional) (out): row in @tree_model + * + * Obtains a @tree_model and @path from value of target type + * %GTK_TYPE_TREE_ROW_DATA. + * + * The returned path must be freed with gtk_tree_path_free(). + * + * Returns: %TRUE if @selection_data had target type %GTK_TYPE_TREE_ROW_DATA + * is otherwise valid + * + * Deprecated: 4.10: Use list models instead + **/ +gboolean +gtk_tree_get_row_drag_data (const GValue *value, + GtkTreeModel **tree_model, + GtkTreePath **path) +{ + GtkTreeRowData *trd; + + g_return_val_if_fail (value != NULL, FALSE); + + if (tree_model) + *tree_model = NULL; + + if (path) + *path = NULL; + + if (!G_VALUE_HOLDS (value, GTK_TYPE_TREE_ROW_DATA)) + return FALSE; + + trd = g_value_get_boxed (value); + if (trd == NULL) + return FALSE; + + if (tree_model) + *tree_model = trd->model; + + if (path) + *path = gtk_tree_path_new_from_string (trd->path); + + return TRUE; +} diff --git a/gtk/deprecated/gtktreednd.h b/gtk/deprecated/gtktreednd.h new file mode 100644 index 0000000000..4fc3433750 --- /dev/null +++ b/gtk/deprecated/gtktreednd.h @@ -0,0 +1,168 @@ +/* gtktreednd.h + * Copyright (C) 2001 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#ifndef __GTK_TREE_DND_H__ +#define __GTK_TREE_DND_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +/** + * GTK_TYPE_TREE_ROW_DATA: + * Magic `GType` to use when dragging rows in a `GtkTreeModel`. + * + * Data in this format will be provided by gtk_tree_create_row_drag_content() + * and can be consumed via gtk_tree_get_row_drag_data(). + */ +#define GTK_TYPE_TREE_ROW_DATA (gtk_tree_row_data_get_type ()) +GDK_DEPRECATED_IN_4_10 +GType gtk_tree_row_data_get_type (void) G_GNUC_CONST; + + +#define GTK_TYPE_TREE_DRAG_SOURCE (gtk_tree_drag_source_get_type ()) +#define GTK_TREE_DRAG_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_TREE_DRAG_SOURCE, GtkTreeDragSource)) +#define GTK_IS_TREE_DRAG_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_TREE_DRAG_SOURCE)) +#define GTK_TREE_DRAG_SOURCE_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GTK_TYPE_TREE_DRAG_SOURCE, GtkTreeDragSourceIface)) + +typedef struct _GtkTreeDragSource GtkTreeDragSource; /* Dummy typedef */ +typedef struct _GtkTreeDragSourceIface GtkTreeDragSourceIface; + +/** + * GtkTreeDragSourceIface: + * @row_draggable: Asks the `GtkTreeDragSource` whether a particular + * row can be used as the source of a DND operation. + * @drag_data_get: Asks the `GtkTreeDragSource` to fill in + * selection_data with a representation of the row at path. + * @drag_data_delete: Asks the `GtkTreeDragSource` to delete the row at + * path, because it was moved somewhere else via drag-and-drop. + */ +struct _GtkTreeDragSourceIface +{ + /*< private >*/ + GTypeInterface g_iface; + + /*< public >*/ + + /* VTable - not signals */ + + gboolean (* row_draggable) (GtkTreeDragSource *drag_source, + GtkTreePath *path); + + GdkContentProvider * (* drag_data_get)(GtkTreeDragSource *drag_source, + GtkTreePath *path); + + gboolean (* drag_data_delete) (GtkTreeDragSource *drag_source, + GtkTreePath *path); +}; + +GDK_DEPRECATED_IN_4_10 +GType gtk_tree_drag_source_get_type (void) G_GNUC_CONST; + +/* Returns whether the given row can be dragged */ +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_drag_source_row_draggable (GtkTreeDragSource *drag_source, + GtkTreePath *path); + +/* Deletes the given row, or returns FALSE if it can't */ +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_drag_source_drag_data_delete (GtkTreeDragSource *drag_source, + GtkTreePath *path); + +/* Fills in selection_data with type selection_data->target based on + * the row denoted by path, returns TRUE if it does anything + */ +GDK_DEPRECATED_IN_4_10 +GdkContentProvider * + gtk_tree_drag_source_drag_data_get (GtkTreeDragSource *drag_source, + GtkTreePath *path); + +#define GTK_TYPE_TREE_DRAG_DEST (gtk_tree_drag_dest_get_type ()) +#define GTK_TREE_DRAG_DEST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_TREE_DRAG_DEST, GtkTreeDragDest)) +#define GTK_IS_TREE_DRAG_DEST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_TREE_DRAG_DEST)) +#define GTK_TREE_DRAG_DEST_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GTK_TYPE_TREE_DRAG_DEST, GtkTreeDragDestIface)) + +typedef struct _GtkTreeDragDest GtkTreeDragDest; /* Dummy typedef */ +typedef struct _GtkTreeDragDestIface GtkTreeDragDestIface; + +/** + * GtkTreeDragDestIface: + * @drag_data_received: Asks the `GtkTreeDragDest` to insert a row + * before the path dest, deriving the contents of the row from + * selection_data. + * @row_drop_possible: Determines whether a drop is possible before + * the given dest_path, at the same depth as dest_path. + */ +struct _GtkTreeDragDestIface +{ + /*< private >*/ + GTypeInterface g_iface; + + /*< public >*/ + + /* VTable - not signals */ + + gboolean (* drag_data_received) (GtkTreeDragDest *drag_dest, + GtkTreePath *dest, + const GValue *value); + + gboolean (* row_drop_possible) (GtkTreeDragDest *drag_dest, + GtkTreePath *dest_path, + const GValue *value); +}; + +GDK_DEPRECATED_IN_4_10 +GType gtk_tree_drag_dest_get_type (void) G_GNUC_CONST; + +/* Inserts a row before dest which contains data in selection_data, + * or returns FALSE if it can't + */ +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_drag_dest_drag_data_received (GtkTreeDragDest *drag_dest, + GtkTreePath *dest, + const GValue *value); + + +/* Returns TRUE if we can drop before path; path may not exist. */ +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_drag_dest_row_drop_possible (GtkTreeDragDest *drag_dest, + GtkTreePath *dest_path, + const GValue *value); + + +/* The selection data would normally have target type GTK_TREE_MODEL_ROW in this + * case. If the target is wrong these functions return FALSE. + */ +GDK_DEPRECATED_IN_4_10 +GdkContentProvider * + gtk_tree_create_row_drag_content (GtkTreeModel *tree_model, + GtkTreePath *path); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_get_row_drag_data (const GValue *value, + GtkTreeModel **tree_model, + GtkTreePath **path); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkTreeDragDest, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkTreeDragSource, g_object_unref) + +G_END_DECLS + +#endif /* __GTK_TREE_DND_H__ */ diff --git a/gtk/deprecated/gtktreemodel.c b/gtk/deprecated/gtktreemodel.c new file mode 100644 index 0000000000..148d0178b8 --- /dev/null +++ b/gtk/deprecated/gtktreemodel.c @@ -0,0 +1,2709 @@ +/* gtktreemodel.c + * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include "gtktreemodel.h" +#include "gtktreeview.h" +#include "gtktreeprivate.h" +#include "gtkmarshalers.h" +#include "gtkprivate.h" + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + +/** + * GtkTreeModel: + * + * The tree interface used by GtkTreeView + * + * The `GtkTreeModel` interface defines a generic tree interface for + * use by the `GtkTreeView` widget. It is an abstract interface, and + * is designed to be usable with any appropriate data structure. The + * programmer just has to implement this interface on their own data + * type for it to be viewable by a `GtkTreeView` widget. + * + * The model is represented as a hierarchical tree of strongly-typed, + * columned data. In other words, the model can be seen as a tree where + * every node has different values depending on which column is being + * queried. The type of data found in a column is determined by using + * the GType system (ie. %G_TYPE_INT, %GTK_TYPE_BUTTON, %G_TYPE_POINTER, + * etc). The types are homogeneous per column across all nodes. It is + * important to note that this interface only provides a way of examining + * a model and observing changes. The implementation of each individual + * model decides how and if changes are made. + * + * In order to make life simpler for programmers who do not need to + * write their own specialized model, two generic models are provided + * — the `GtkTreeStore` and the `GtkListStore`. To use these, the + * developer simply pushes data into these models as necessary. These + * models provide the data structure as well as all appropriate tree + * interfaces. As a result, implementing drag and drop, sorting, and + * storing data is trivial. For the vast majority of trees and lists, + * these two models are sufficient. + * + * Models are accessed on a node/column level of granularity. One can + * query for the value of a model at a certain node and a certain + * column on that node. There are two structures used to reference a + * particular node in a model. They are the [struct@Gtk.TreePath] and + * the [struct@Gtk.TreeIter] (“iter” is short for iterator). Most of the + * interface consists of operations on a [struct@Gtk.TreeIter]. + * + * A path is essentially a potential node. It is a location on a model + * that may or may not actually correspond to a node on a specific + * model. A [struct@Gtk.TreePath] can be converted into either an + * array of unsigned integers or a string. The string form is a list + * of numbers separated by a colon. Each number refers to the offset + * at that level. Thus, the path `0` refers to the root + * node and the path `2:4` refers to the fifth child of + * the third node. + * + * By contrast, a [struct@Gtk.TreeIter] is a reference to a specific node on + * a specific model. It is a generic struct with an integer and three + * generic pointers. These are filled in by the model in a model-specific + * way. One can convert a path to an iterator by calling + * gtk_tree_model_get_iter(). These iterators are the primary way + * of accessing a model and are similar to the iterators used by + * `GtkTextBuffer`. They are generally statically allocated on the + * stack and only used for a short time. The model interface defines + * a set of operations using them for navigating the model. + * + * It is expected that models fill in the iterator with private data. + * For example, the `GtkListStore` model, which is internally a simple + * linked list, stores a list node in one of the pointers. The + * `GtkTreeModel`Sort stores an array and an offset in two of the + * pointers. Additionally, there is an integer field. This field is + * generally filled with a unique stamp per model. This stamp is for + * catching errors resulting from using invalid iterators with a model. + * + * The lifecycle of an iterator can be a little confusing at first. + * Iterators are expected to always be valid for as long as the model + * is unchanged (and doesn’t emit a signal). The model is considered + * to own all outstanding iterators and nothing needs to be done to + * free them from the user’s point of view. Additionally, some models + * guarantee that an iterator is valid for as long as the node it refers + * to is valid (most notably the `GtkTreeStore` and `GtkListStore`). + * Although generally uninteresting, as one always has to allow for + * the case where iterators do not persist beyond a signal, some very + * important performance enhancements were made in the sort model. + * As a result, the %GTK_TREE_MODEL_ITERS_PERSIST flag was added to + * indicate this behavior. + * + * To help show some common operation of a model, some examples are + * provided. The first example shows three ways of getting the iter at + * the location `3:2:5`. While the first method shown is + * easier, the second is much more common, as you often get paths from + * callbacks. + * + * ## Acquiring a `GtkTreeIter` + * + * ```c + * // Three ways of getting the iter pointing to the location + * GtkTreePath *path; + * GtkTreeIter iter; + * GtkTreeIter parent_iter; + * + * // get the iterator from a string + * gtk_tree_model_get_iter_from_string (model, + * &iter, + * "3:2:5"); + * + * // get the iterator from a path + * path = gtk_tree_path_new_from_string ("3:2:5"); + * gtk_tree_model_get_iter (model, &iter, path); + * gtk_tree_path_free (path); + * + * // walk the tree to find the iterator + * gtk_tree_model_iter_nth_child (model, &iter, + * NULL, 3); + * parent_iter = iter; + * gtk_tree_model_iter_nth_child (model, &iter, + * &parent_iter, 2); + * parent_iter = iter; + * gtk_tree_model_iter_nth_child (model, &iter, + * &parent_iter, 5); + * ``` + * + * This second example shows a quick way of iterating through a list + * and getting a string and an integer from each row. The + * populate_model() function used below is not + * shown, as it is specific to the `GtkListStore`. For information on + * how to write such a function, see the `GtkListStore` documentation. + * + * ## Reading data from a `GtkTreeModel` + * + * ```c + * enum + * { + * STRING_COLUMN, + * INT_COLUMN, + * N_COLUMNS + * }; + * + * ... + * + * GtkTreeModel *list_store; + * GtkTreeIter iter; + * gboolean valid; + * int row_count = 0; + * + * // make a new list_store + * list_store = gtk_list_store_new (N_COLUMNS, + * G_TYPE_STRING, + * G_TYPE_INT); + * + * // Fill the list store with data + * populate_model (list_store); + * + * // Get the first iter in the list, check it is valid and walk + * // through the list, reading each row. + * + * valid = gtk_tree_model_get_iter_first (list_store, + * &iter); + * while (valid) + * { + * char *str_data; + * int int_data; + * + * // Make sure you terminate calls to gtk_tree_model_get() with a “-1” value + * gtk_tree_model_get (list_store, &iter, + * STRING_COLUMN, &str_data, + * INT_COLUMN, &int_data, + * -1); + * + * // Do something with the data + * g_print ("Row %d: (%s,%d)\n", + * row_count, str_data, int_data); + * g_free (str_data); + * + * valid = gtk_tree_model_iter_next (list_store, + * &iter); + * row_count++; + * } + * ``` + * + * The `GtkTreeModel` interface contains two methods for reference + * counting: gtk_tree_model_ref_node() and gtk_tree_model_unref_node(). + * These two methods are optional to implement. The reference counting + * is meant as a way for views to let models know when nodes are being + * displayed. `GtkTreeView` will take a reference on a node when it is + * visible, which means the node is either in the toplevel or expanded. + * Being displayed does not mean that the node is currently directly + * visible to the user in the viewport. Based on this reference counting + * scheme a caching model, for example, can decide whether or not to cache + * a node based on the reference count. A file-system based model would + * not want to keep the entire file hierarchy in memory, but just the + * folders that are currently expanded in every current view. + * + * When working with reference counting, the following rules must be taken + * into account: + * + * - Never take a reference on a node without owning a reference on its parent. + * This means that all parent nodes of a referenced node must be referenced + * as well. + * + * - Outstanding references on a deleted node are not released. This is not + * possible because the node has already been deleted by the time the + * row-deleted signal is received. + * + * - Models are not obligated to emit a signal on rows of which none of its + * siblings are referenced. To phrase this differently, signals are only + * required for levels in which nodes are referenced. For the root level + * however, signals must be emitted at all times (however the root level + * is always referenced when any view is attached). + */ + +#define INITIALIZE_TREE_ITER(Iter) \ + G_STMT_START{ \ + (Iter)->stamp = 0; \ + (Iter)->user_data = NULL; \ + (Iter)->user_data2 = NULL; \ + (Iter)->user_data3 = NULL; \ + }G_STMT_END + +#define ROW_REF_DATA_STRING "gtk-tree-row-refs" + +enum { + ROW_CHANGED, + ROW_INSERTED, + ROW_HAS_CHILD_TOGGLED, + ROW_DELETED, + ROWS_REORDERED, + LAST_SIGNAL +}; + +static guint tree_model_signals[LAST_SIGNAL] = { 0 }; + +/** + * GtkTreePath: + * + * An opaque structure representing a path to a row in a model. + */ +struct _GtkTreePath +{ + int depth; /* Number of elements */ + int alloc; /* Number of allocated elements */ + int *indices; +}; + +typedef struct +{ + GSList *list; +} RowRefList; + +static void gtk_tree_model_base_init (gpointer g_class); + +/* custom closures */ +static void row_inserted_marshal (GClosure *closure, + GValue /* out */ *return_value, + guint n_param_value, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +static void row_deleted_marshal (GClosure *closure, + GValue /* out */ *return_value, + guint n_param_value, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +static void rows_reordered_marshal (GClosure *closure, + GValue /* out */ *return_value, + guint n_param_value, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +static void gtk_tree_row_ref_inserted (RowRefList *refs, + GtkTreePath *path, + GtkTreeIter *iter); +static void gtk_tree_row_ref_deleted (RowRefList *refs, + GtkTreePath *path); +static void gtk_tree_row_ref_reordered (RowRefList *refs, + GtkTreePath *path, + GtkTreeIter *iter, + int *new_order); + +GType +gtk_tree_model_get_type (void) +{ + static GType tree_model_type = 0; + + if (! tree_model_type) + { + const GTypeInfo tree_model_info = + { + sizeof (GtkTreeModelIface), /* class_size */ + gtk_tree_model_base_init, /* base_init */ + NULL, /* base_finalize */ + NULL, + NULL, /* class_finalize */ + NULL, /* class_data */ + 0, + 0, /* n_preallocs */ + NULL + }; + + tree_model_type = + g_type_register_static (G_TYPE_INTERFACE, I_("GtkTreeModel"), + &tree_model_info, 0); + + g_type_interface_add_prerequisite (tree_model_type, G_TYPE_OBJECT); + } + + return tree_model_type; +} + +static void +gtk_tree_model_base_init (gpointer g_class) +{ + static gboolean initialized = FALSE; + GClosure *closure; + + if (! initialized) + { + GType row_inserted_params[2]; + GType row_deleted_params[1]; + GType rows_reordered_params[3]; + + row_inserted_params[0] = GTK_TYPE_TREE_PATH | G_SIGNAL_TYPE_STATIC_SCOPE; + row_inserted_params[1] = GTK_TYPE_TREE_ITER; + + row_deleted_params[0] = GTK_TYPE_TREE_PATH | G_SIGNAL_TYPE_STATIC_SCOPE; + + rows_reordered_params[0] = GTK_TYPE_TREE_PATH | G_SIGNAL_TYPE_STATIC_SCOPE; + rows_reordered_params[1] = GTK_TYPE_TREE_ITER; + rows_reordered_params[2] = G_TYPE_POINTER; + + /** + * GtkTreeModel::row-changed: + * @tree_model: the `GtkTreeModel` on which the signal is emitted + * @path: a `GtkTreePath` identifying the changed row + * @iter: a valid `GtkTreeIter` pointing to the changed row + * + * This signal is emitted when a row in the model has changed. + */ + tree_model_signals[ROW_CHANGED] = + g_signal_new (I_("row-changed"), + GTK_TYPE_TREE_MODEL, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkTreeModelIface, row_changed), + NULL, NULL, + _gtk_marshal_VOID__BOXED_BOXED, + G_TYPE_NONE, 2, + GTK_TYPE_TREE_PATH | G_SIGNAL_TYPE_STATIC_SCOPE, + GTK_TYPE_TREE_ITER); + g_signal_set_va_marshaller (tree_model_signals[ROW_CHANGED], + G_TYPE_FROM_CLASS (g_class), + _gtk_marshal_VOID__BOXED_BOXEDv); + + /* We need to get notification about structure changes + * to update row references., so instead of using the + * standard g_signal_new() with an offset into our interface + * structure, we use a customs closures for the class + * closures (default handlers) that first update row references + * and then calls the function from the interface structure. + * + * The reason we don't simply update the row references from + * the wrapper functions (gtk_tree_model_row_inserted(), etc.) + * is to keep proper ordering with respect to signal handlers + * connected normally and after. + */ + + /** + * GtkTreeModel::row-inserted: + * @tree_model: the `GtkTreeModel` on which the signal is emitted + * @path: a `GtkTreePath` identifying the new row + * @iter: a valid `GtkTreeIter` pointing to the new row + * + * This signal is emitted when a new row has been inserted in + * the model. + * + * Note that the row may still be empty at this point, since + * it is a common pattern to first insert an empty row, and + * then fill it with the desired values. + */ + closure = g_closure_new_simple (sizeof (GClosure), NULL); + g_closure_set_marshal (closure, row_inserted_marshal); + tree_model_signals[ROW_INSERTED] = + g_signal_newv (I_("row-inserted"), + GTK_TYPE_TREE_MODEL, + G_SIGNAL_RUN_FIRST, + closure, + NULL, NULL, + _gtk_marshal_VOID__BOXED_BOXED, + G_TYPE_NONE, 2, + row_inserted_params); + g_signal_set_va_marshaller (tree_model_signals[ROW_INSERTED], + G_TYPE_FROM_CLASS (g_class), + _gtk_marshal_VOID__BOXED_BOXEDv); + + /** + * GtkTreeModel::row-has-child-toggled: + * @tree_model: the `GtkTreeModel` on which the signal is emitted + * @path: a `GtkTreePath` identifying the row + * @iter: a valid `GtkTreeIter` pointing to the row + * + * This signal is emitted when a row has gotten the first child + * row or lost its last child row. + */ + tree_model_signals[ROW_HAS_CHILD_TOGGLED] = + g_signal_new (I_("row-has-child-toggled"), + GTK_TYPE_TREE_MODEL, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkTreeModelIface, row_has_child_toggled), + NULL, NULL, + _gtk_marshal_VOID__BOXED_BOXED, + G_TYPE_NONE, 2, + GTK_TYPE_TREE_PATH | G_SIGNAL_TYPE_STATIC_SCOPE, + GTK_TYPE_TREE_ITER); + g_signal_set_va_marshaller (tree_model_signals[ROW_HAS_CHILD_TOGGLED], + G_TYPE_FROM_CLASS (g_class), + _gtk_marshal_VOID__BOXED_BOXEDv); + + /** + * GtkTreeModel::row-deleted: + * @tree_model: the `GtkTreeModel` on which the signal is emitted + * @path: a `GtkTreePath` identifying the row + * + * This signal is emitted when a row has been deleted. + * + * Note that no iterator is passed to the signal handler, + * since the row is already deleted. + * + * This should be called by models after a row has been removed. + * The location pointed to by @path should be the location that + * the row previously was at. It may not be a valid location anymore. + */ + closure = g_closure_new_simple (sizeof (GClosure), NULL); + g_closure_set_marshal (closure, row_deleted_marshal); + tree_model_signals[ROW_DELETED] = + g_signal_newv (I_("row-deleted"), + GTK_TYPE_TREE_MODEL, + G_SIGNAL_RUN_FIRST, + closure, + NULL, NULL, + NULL, + G_TYPE_NONE, 1, + row_deleted_params); + + /** + * GtkTreeModel::rows-reordered: (skip) + * @tree_model: the `GtkTreeModel` on which the signal is emitted + * @path: a `GtkTreePath` identifying the tree node whose children + * have been reordered + * @iter: a valid `GtkTreeIter` pointing to the node whose children + * have been reordered, or %NULL if the depth of @path is 0 + * @new_order: an array of integers mapping the current position + * of each child to its old position before the re-ordering, + * i.e. @new_order`[newpos] = oldpos` + * + * This signal is emitted when the children of a node in the + * `GtkTreeModel` have been reordered. + * + * Note that this signal is not emitted + * when rows are reordered by DND, since this is implemented + * by removing and then reinserting the row. + */ + closure = g_closure_new_simple (sizeof (GClosure), NULL); + g_closure_set_marshal (closure, rows_reordered_marshal); + tree_model_signals[ROWS_REORDERED] = + g_signal_newv (I_("rows-reordered"), + GTK_TYPE_TREE_MODEL, + G_SIGNAL_RUN_FIRST, + closure, + NULL, NULL, + _gtk_marshal_VOID__BOXED_BOXED_POINTER, + G_TYPE_NONE, 3, + rows_reordered_params); + g_signal_set_va_marshaller (tree_model_signals[ROWS_REORDERED], + G_TYPE_FROM_CLASS (g_class), + _gtk_marshal_VOID__BOXED_BOXED_POINTERv); + initialized = TRUE; + } +} + +static void +row_inserted_marshal (GClosure *closure, + GValue /* out */ *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + GtkTreeModelIface *iface; + + void (* row_inserted_callback) (GtkTreeModel *tree_model, + GtkTreePath *path, + GtkTreeIter *iter) = NULL; + + GObject *model = g_value_get_object (param_values + 0); + GtkTreePath *path = (GtkTreePath *)g_value_get_boxed (param_values + 1); + GtkTreeIter *iter = (GtkTreeIter *)g_value_get_boxed (param_values + 2); + + /* first, we need to update internal row references */ + gtk_tree_row_ref_inserted ((RowRefList *)g_object_get_data (model, ROW_REF_DATA_STRING), + path, iter); + + /* fetch the interface ->row_inserted implementation */ + iface = GTK_TREE_MODEL_GET_IFACE (model); + row_inserted_callback = G_STRUCT_MEMBER (gpointer, iface, + G_STRUCT_OFFSET (GtkTreeModelIface, + row_inserted)); + + /* Call that default signal handler, it if has been set */ + if (row_inserted_callback) + row_inserted_callback (GTK_TREE_MODEL (model), path, iter); +} + +static void +row_deleted_marshal (GClosure *closure, + GValue /* out */ *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + GtkTreeModelIface *iface; + void (* row_deleted_callback) (GtkTreeModel *tree_model, + GtkTreePath *path) = NULL; + GObject *model = g_value_get_object (param_values + 0); + GtkTreePath *path = (GtkTreePath *)g_value_get_boxed (param_values + 1); + + /* first, we need to update internal row references */ + gtk_tree_row_ref_deleted ((RowRefList *)g_object_get_data (model, ROW_REF_DATA_STRING), + path); + + /* fetch the interface ->row_deleted implementation */ + iface = GTK_TREE_MODEL_GET_IFACE (model); + row_deleted_callback = G_STRUCT_MEMBER (gpointer, iface, + G_STRUCT_OFFSET (GtkTreeModelIface, + row_deleted)); + + /* Call that default signal handler, it if has been set */ + if (row_deleted_callback) + row_deleted_callback (GTK_TREE_MODEL (model), path); +} + +static void +rows_reordered_marshal (GClosure *closure, + GValue /* out */ *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + GtkTreeModelIface *iface; + void (* rows_reordered_callback) (GtkTreeModel *tree_model, + GtkTreePath *path, + GtkTreeIter *iter, + int *new_order); + + GObject *model = g_value_get_object (param_values + 0); + GtkTreePath *path = (GtkTreePath *)g_value_get_boxed (param_values + 1); + GtkTreeIter *iter = (GtkTreeIter *)g_value_get_boxed (param_values + 2); + int *new_order = (int *)g_value_get_pointer (param_values + 3); + + /* first, we need to update internal row references */ + gtk_tree_row_ref_reordered ((RowRefList *)g_object_get_data (model, ROW_REF_DATA_STRING), + path, iter, new_order); + + /* fetch the interface ->rows_reordered implementation */ + iface = GTK_TREE_MODEL_GET_IFACE (model); + rows_reordered_callback = G_STRUCT_MEMBER (gpointer, iface, + G_STRUCT_OFFSET (GtkTreeModelIface, + rows_reordered)); + + /* Call that default signal handler, it if has been set */ + if (rows_reordered_callback) + rows_reordered_callback (GTK_TREE_MODEL (model), path, iter, new_order); +} + +/** + * gtk_tree_path_new: + * + * Creates a new `GtkTreePath` + * This refers to a row. + * + * Returns: A newly created `GtkTreePath`. + * + * Deprecated: 4.10 + */ +GtkTreePath * +gtk_tree_path_new (void) +{ + GtkTreePath *retval; + retval = g_slice_new (GtkTreePath); + retval->depth = 0; + retval->alloc = 0; + retval->indices = NULL; + + return retval; +} + +/** + * gtk_tree_path_new_from_string: + * @path: The string representation of a path + * + * Creates a new `GtkTreePath` initialized to @path. + * + * @path is expected to be a colon separated list of numbers. + * For example, the string “10:4:0” would create a path of depth + * 3 pointing to the 11th child of the root node, the 5th + * child of that 11th child, and the 1st child of that 5th child. + * If an invalid path string is passed in, %NULL is returned. + * + * Returns: (nullable): A newly-created `GtkTreePath` + * + * Deprecated: 4.10 + */ +GtkTreePath * +gtk_tree_path_new_from_string (const char *path) +{ + GtkTreePath *retval; + const char *orig_path = path; + char *ptr; + int i; + + g_return_val_if_fail (path != NULL, NULL); + g_return_val_if_fail (*path != '\000', NULL); + + retval = gtk_tree_path_new (); + + while (1) + { + i = strtol (path, &ptr, 10); + if (i < 0) + { + g_warning (G_STRLOC ": Negative numbers in path %s passed to gtk_tree_path_new_from_string", orig_path); + gtk_tree_path_free (retval); + return NULL; + } + + gtk_tree_path_append_index (retval, i); + + if (*ptr == '\000') + break; + if (ptr == path || *ptr != ':') + { + g_warning (G_STRLOC ": Invalid path %s passed to gtk_tree_path_new_from_string", orig_path); + gtk_tree_path_free (retval); + return NULL; + } + path = ptr + 1; + } + + return retval; +} + +/** + * gtk_tree_path_new_from_indices: + * @first_index: first integer + * @...: list of integers terminated by -1 + * + * Creates a new path with @first_index and @varargs as indices. + * + * Returns: A newly created `GtkTreePath` + * + * Deprecated: 4.10 + */ +GtkTreePath * +gtk_tree_path_new_from_indices (int first_index, + ...) +{ + int arg; + va_list args; + GtkTreePath *path; + + path = gtk_tree_path_new (); + + va_start (args, first_index); + arg = first_index; + + while (arg != -1) + { + gtk_tree_path_append_index (path, arg); + arg = va_arg (args, int); + } + + va_end (args); + + return path; +} + +/** + * gtk_tree_path_new_from_indicesv: (rename-to gtk_tree_path_new_from_indices) + * @indices: (array length=length): array of indices + * @length: length of @indices array + * + * Creates a new path with the given @indices array of @length. + * + * Returns: A newly created `GtkTreePath` + * + * Deprecated: 4.10 + */ +GtkTreePath * +gtk_tree_path_new_from_indicesv (int *indices, + gsize length) +{ + GtkTreePath *path; + + g_return_val_if_fail (indices != NULL && length != 0, NULL); + + path = gtk_tree_path_new (); + path->alloc = length; + path->depth = length; + path->indices = g_new (int, length); + memcpy (path->indices, indices, length * sizeof (int)); + + return path; +} + +/** + * gtk_tree_path_to_string: + * @path: a `GtkTreePath` + * + * Generates a string representation of the path. + * + * This string is a “:” separated list of numbers. + * For example, “4:10:0:3” would be an acceptable + * return value for this string. If the path has + * depth 0, %NULL is returned. + * + * Returns: (nullable): A newly-allocated string + * + * Deprecated: 4.10 + */ +char * +gtk_tree_path_to_string (GtkTreePath *path) +{ + char *retval, *ptr, *end; + int i, n; + + g_return_val_if_fail (path != NULL, NULL); + + if (path->depth == 0) + return NULL; + + n = path->depth * 12; + ptr = retval = g_new0 (char, n); + end = ptr + n; + g_snprintf (retval, end - ptr, "%d", path->indices[0]); + while (*ptr != '\000') + ptr++; + + for (i = 1; i < path->depth; i++) + { + g_snprintf (ptr, end - ptr, ":%d", path->indices[i]); + while (*ptr != '\000') + ptr++; + } + + return retval; +} + +/** + * gtk_tree_path_new_first: + * + * Creates a new `GtkTreePath`. + * + * The string representation of this path is “0”. + * + * Returns: A new `GtkTreePath` + * + * Deprecated: 4.10 + */ +GtkTreePath * +gtk_tree_path_new_first (void) +{ + GtkTreePath *retval; + + retval = gtk_tree_path_new (); + gtk_tree_path_append_index (retval, 0); + + return retval; +} + +/** + * gtk_tree_path_append_index: + * @path: a `GtkTreePath` + * @index_: the index + * + * Appends a new index to a path. + * + * As a result, the depth of the path is increased. + * + * Deprecated: 4.10 + */ +void +gtk_tree_path_append_index (GtkTreePath *path, + int index_) +{ + g_return_if_fail (path != NULL); + g_return_if_fail (index_ >= 0); + + if (path->depth == path->alloc) + { + path->alloc = MAX (path->alloc * 2, 1); + path->indices = g_renew (int, path->indices, path->alloc); + } + + path->depth += 1; + path->indices[path->depth - 1] = index_; +} + +/** + * gtk_tree_path_prepend_index: + * @path: a `GtkTreePath` + * @index_: the index + * + * Prepends a new index to a path. + * + * As a result, the depth of the path is increased. + * + * Deprecated: 4.10 + */ +void +gtk_tree_path_prepend_index (GtkTreePath *path, + int index) +{ + if (path->depth == path->alloc) + { + int *indices; + path->alloc = MAX (path->alloc * 2, 1); + indices = g_new (int, path->alloc); + memcpy (indices + 1, path->indices, path->depth * sizeof (int)); + g_free (path->indices); + path->indices = indices; + } + else if (path->depth > 0) + memmove (path->indices + 1, path->indices, path->depth * sizeof (int)); + + path->depth += 1; + path->indices[0] = index; +} + +/** + * gtk_tree_path_get_depth: + * @path: a `GtkTreePath` + * + * Returns the current depth of @path. + * + * Returns: The depth of @path + * + * Deprecated: 4.10 + */ +int +gtk_tree_path_get_depth (GtkTreePath *path) +{ + g_return_val_if_fail (path != NULL, 0); + + return path->depth; +} + +/** + * gtk_tree_path_get_indices: (skip) + * @path: a `GtkTreePath` + * + * Returns the current indices of @path. + * + * This is an array of integers, each representing a node in a tree. + * This value should not be freed. + * + * The length of the array can be obtained with gtk_tree_path_get_depth(). + * + * Returns: (nullable) (transfer none): The current indices + * + * Deprecated: 4.10 + */ +int * +gtk_tree_path_get_indices (GtkTreePath *path) +{ + g_return_val_if_fail (path != NULL, NULL); + + return path->indices; +} + +/** + * gtk_tree_path_get_indices_with_depth: (rename-to gtk_tree_path_get_indices) + * @path: a `GtkTreePath` + * @depth: (out) (optional): return location for number of elements + * returned in the integer array + * + * Returns the current indices of @path. + * + * This is an array of integers, each representing a node in a tree. + * It also returns the number of elements in the array. + * The array should not be freed. + * + * Returns: (array length=depth) (transfer none) (nullable): The current + * indices + * + * Deprecated: 4.10 + */ +int * +gtk_tree_path_get_indices_with_depth (GtkTreePath *path, + int *depth) +{ + g_return_val_if_fail (path != NULL, NULL); + + if (depth) + *depth = path->depth; + + return path->indices; +} + +/** + * gtk_tree_path_free: + * @path: (nullable): a `GtkTreePath` + * + * Frees @path. If @path is %NULL, it simply returns. + * + * Deprecated: 4.10 + */ +void +gtk_tree_path_free (GtkTreePath *path) +{ + if (!path) + return; + + g_free (path->indices); + g_slice_free (GtkTreePath, path); +} + +/** + * gtk_tree_path_copy: + * @path: a `GtkTreePath` + * + * Creates a new `GtkTreePath` as a copy of @path. + * + * Returns: a new `GtkTreePath` + * + * Deprecated: 4.10 + */ +GtkTreePath * +gtk_tree_path_copy (const GtkTreePath *path) +{ + GtkTreePath *retval; + + g_return_val_if_fail (path != NULL, NULL); + + retval = g_slice_new (GtkTreePath); + retval->depth = path->depth; + retval->alloc = retval->depth; + retval->indices = g_new (int, path->alloc); + memcpy (retval->indices, path->indices, path->depth * sizeof (int)); + return retval; +} + +G_DEFINE_BOXED_TYPE (GtkTreePath, gtk_tree_path, + gtk_tree_path_copy, + gtk_tree_path_free) + +/** + * gtk_tree_path_compare: + * @a: a `GtkTreePath` + * @b: a `GtkTreePath` to compare with + * + * Compares two paths. + * + * If @a appears before @b in a tree, then -1 is returned. + * If @b appears before @a, then 1 is returned. + * If the two nodes are equal, then 0 is returned. + * + * Returns: the relative positions of @a and @b + * + * Deprecated: 4.10 + */ +int +gtk_tree_path_compare (const GtkTreePath *a, + const GtkTreePath *b) +{ + int p = 0, q = 0; + + g_return_val_if_fail (a != NULL, 0); + g_return_val_if_fail (b != NULL, 0); + g_return_val_if_fail (a->depth > 0, 0); + g_return_val_if_fail (b->depth > 0, 0); + + do + { + if (a->indices[p] == b->indices[q]) + continue; + return (a->indices[p] < b->indices[q]?-1:1); + } + while (++p < a->depth && ++q < b->depth); + if (a->depth == b->depth) + return 0; + return (a->depth < b->depth?-1:1); +} + +/** + * gtk_tree_path_is_ancestor: + * @path: a `GtkTreePath` + * @descendant: another `GtkTreePath` + * + * Returns %TRUE if @descendant is a descendant of @path. + * + * Returns: %TRUE if @descendant is contained inside @path + * + * Deprecated: 4.10 + */ +gboolean +gtk_tree_path_is_ancestor (GtkTreePath *path, + GtkTreePath *descendant) +{ + int i; + + g_return_val_if_fail (path != NULL, FALSE); + g_return_val_if_fail (descendant != NULL, FALSE); + + /* can't be an ancestor if we're deeper */ + if (path->depth >= descendant->depth) + return FALSE; + + i = 0; + while (i < path->depth) + { + if (path->indices[i] != descendant->indices[i]) + return FALSE; + ++i; + } + + return TRUE; +} + +/** + * gtk_tree_path_is_descendant: + * @path: a `GtkTreePath` + * @ancestor: another `GtkTreePath` + * + * Returns %TRUE if @path is a descendant of @ancestor. + * + * Returns: %TRUE if @ancestor contains @path somewhere below it + * + * Deprecated: 4.10 + */ +gboolean +gtk_tree_path_is_descendant (GtkTreePath *path, + GtkTreePath *ancestor) +{ + int i; + + g_return_val_if_fail (path != NULL, FALSE); + g_return_val_if_fail (ancestor != NULL, FALSE); + + /* can't be a descendant if we're shallower in the tree */ + if (path->depth <= ancestor->depth) + return FALSE; + + i = 0; + while (i < ancestor->depth) + { + if (path->indices[i] != ancestor->indices[i]) + return FALSE; + ++i; + } + + return TRUE; +} + + +/** + * gtk_tree_path_next: + * @path: a `GtkTreePath` + * + * Moves the @path to point to the next node at the current depth. + * + * Deprecated: 4.10 + */ +void +gtk_tree_path_next (GtkTreePath *path) +{ + g_return_if_fail (path != NULL); + g_return_if_fail (path->depth > 0); + + path->indices[path->depth - 1] ++; +} + +/** + * gtk_tree_path_prev: + * @path: a `GtkTreePath` + * + * Moves the @path to point to the previous node at the + * current depth, if it exists. + * + * Returns: %TRUE if @path has a previous node, and + * the move was made + * + * Deprecated: 4.10 + */ +gboolean +gtk_tree_path_prev (GtkTreePath *path) +{ + g_return_val_if_fail (path != NULL, FALSE); + + if (path->depth == 0) + return FALSE; + + if (path->indices[path->depth - 1] == 0) + return FALSE; + + path->indices[path->depth - 1] -= 1; + + return TRUE; +} + +/** + * gtk_tree_path_up: + * @path: a `GtkTreePath` + * + * Moves the @path to point to its parent node, if it has a parent. + * + * Returns: %TRUE if @path has a parent, and the move was made + * + * Deprecated: 4.10 + */ +gboolean +gtk_tree_path_up (GtkTreePath *path) +{ + g_return_val_if_fail (path != NULL, FALSE); + + if (path->depth == 0) + return FALSE; + + path->depth--; + + return TRUE; +} + +/** + * gtk_tree_path_down: + * @path: a `GtkTreePath` + * + * Moves @path to point to the first child of the current path. + * + * Deprecated: 4.10 + */ +void +gtk_tree_path_down (GtkTreePath *path) +{ + g_return_if_fail (path != NULL); + + gtk_tree_path_append_index (path, 0); +} + +/** + * gtk_tree_iter_copy: + * @iter: a `GtkTreeIter` + * + * Creates a dynamically allocated tree iterator as a copy of @iter. + * + * This function is not intended for use in applications, + * because you can just copy the structs by value + * (`GtkTreeIter new_iter = iter;`). + * You must free this iter with gtk_tree_iter_free(). + * + * Returns: a newly-allocated copy of @iter + * + * Deprecated: 4.10 + */ +GtkTreeIter * +gtk_tree_iter_copy (GtkTreeIter *iter) +{ + GtkTreeIter *retval; + + g_return_val_if_fail (iter != NULL, NULL); + + retval = g_slice_new (GtkTreeIter); + *retval = *iter; + + return retval; +} + +/** + * gtk_tree_iter_free: + * @iter: a dynamically allocated tree iterator + * + * Frees an iterator that has been allocated by gtk_tree_iter_copy(). + * + * This function is mainly used for language bindings. + * + * Deprecated: 4.10 + */ +void +gtk_tree_iter_free (GtkTreeIter *iter) +{ + g_return_if_fail (iter != NULL); + + g_slice_free (GtkTreeIter, iter); +} + +G_DEFINE_BOXED_TYPE (GtkTreeIter, gtk_tree_iter, + gtk_tree_iter_copy, + gtk_tree_iter_free) + +/** + * gtk_tree_model_get_flags: + * @tree_model: a `GtkTreeModel` + * + * Returns a set of flags supported by this interface. + * + * The flags are a bitwise combination of `GtkTreeModel`Flags. + * The flags supported should not change during the lifetime + * of the @tree_model. + * + * Returns: the flags supported by this interface + * + * Deprecated: 4.10 + */ +GtkTreeModelFlags +gtk_tree_model_get_flags (GtkTreeModel *tree_model) +{ + GtkTreeModelIface *iface; + + g_return_val_if_fail (GTK_IS_TREE_MODEL (tree_model), 0); + + iface = GTK_TREE_MODEL_GET_IFACE (tree_model); + if (iface->get_flags) + return (* iface->get_flags) (tree_model); + + return 0; +} + +/** + * gtk_tree_model_get_n_columns: + * @tree_model: a `GtkTreeModel` + * + * Returns the number of columns supported by @tree_model. + * + * Returns: the number of columns + * + * Deprecated: 4.10 + */ +int +gtk_tree_model_get_n_columns (GtkTreeModel *tree_model) +{ + GtkTreeModelIface *iface; + g_return_val_if_fail (GTK_IS_TREE_MODEL (tree_model), 0); + + iface = GTK_TREE_MODEL_GET_IFACE (tree_model); + g_return_val_if_fail (iface->get_n_columns != NULL, 0); + + return (* iface->get_n_columns) (tree_model); +} + +/** + * gtk_tree_model_get_column_type: + * @tree_model: a `GtkTreeModel` + * @index_: the column index + * + * Returns the type of the column. + * + * Returns: the type of the column + * + * Deprecated: 4.10 + */ +GType +gtk_tree_model_get_column_type (GtkTreeModel *tree_model, + int index) +{ + GtkTreeModelIface *iface; + + g_return_val_if_fail (GTK_IS_TREE_MODEL (tree_model), G_TYPE_INVALID); + + iface = GTK_TREE_MODEL_GET_IFACE (tree_model); + g_return_val_if_fail (iface->get_column_type != NULL, G_TYPE_INVALID); + g_return_val_if_fail (index >= 0, G_TYPE_INVALID); + + return (* iface->get_column_type) (tree_model, index); +} + +/** + * gtk_tree_model_get_iter: + * @tree_model: a `GtkTreeModel` + * @iter: (out): the uninitialized `GtkTreeIter` + * @path: the `GtkTreePath` + * + * Sets @iter to a valid iterator pointing to @path. + * + * If @path does not exist, @iter is set to an invalid + * iterator and %FALSE is returned. + * + * Returns: %TRUE, if @iter was set + * + * Deprecated: 4.10 + */ +gboolean +gtk_tree_model_get_iter (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreePath *path) +{ + GtkTreeModelIface *iface; + + g_return_val_if_fail (GTK_IS_TREE_MODEL (tree_model), FALSE); + g_return_val_if_fail (iter != NULL, FALSE); + g_return_val_if_fail (path != NULL, FALSE); + + iface = GTK_TREE_MODEL_GET_IFACE (tree_model); + g_return_val_if_fail (iface->get_iter != NULL, FALSE); + g_return_val_if_fail (path->depth > 0, FALSE); + + INITIALIZE_TREE_ITER (iter); + + return (* iface->get_iter) (tree_model, iter, path); +} + +/** + * gtk_tree_model_get_iter_from_string: + * @tree_model: a `GtkTreeModel` + * @iter: (out): an uninitialized `GtkTreeIter` + * @path_string: a string representation of a `GtkTreePath` + * + * Sets @iter to a valid iterator pointing to @path_string, if it + * exists. + * + * Otherwise, @iter is left invalid and %FALSE is returned. + * + * Returns: %TRUE, if @iter was set + * + * Deprecated: 4.10 + */ +gboolean +gtk_tree_model_get_iter_from_string (GtkTreeModel *tree_model, + GtkTreeIter *iter, + const char *path_string) +{ + gboolean retval; + GtkTreePath *path; + + g_return_val_if_fail (GTK_IS_TREE_MODEL (tree_model), FALSE); + g_return_val_if_fail (iter != NULL, FALSE); + g_return_val_if_fail (path_string != NULL, FALSE); + + path = gtk_tree_path_new_from_string (path_string); + + g_return_val_if_fail (path != NULL, FALSE); + + retval = gtk_tree_model_get_iter (tree_model, iter, path); + gtk_tree_path_free (path); + + return retval; +} + +/** + * gtk_tree_model_get_string_from_iter: + * @tree_model: a `GtkTreeModel` + * @iter: a `GtkTreeIter` + * + * Generates a string representation of the iter. + * + * This string is a “:” separated list of numbers. + * For example, “4:10:0:3” would be an acceptable + * return value for this string. + * + * Returns: (nullable): a newly-allocated string + * + * Deprecated: 4.10 + */ +char * +gtk_tree_model_get_string_from_iter (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + GtkTreePath *path; + char *ret; + + g_return_val_if_fail (GTK_IS_TREE_MODEL (tree_model), NULL); + g_return_val_if_fail (iter != NULL, NULL); + + path = gtk_tree_model_get_path (tree_model, iter); + + g_return_val_if_fail (path != NULL, NULL); + + ret = gtk_tree_path_to_string (path); + gtk_tree_path_free (path); + + return ret; +} + +/** + * gtk_tree_model_get_iter_first: + * @tree_model: a `GtkTreeModel` + * @iter: (out): the uninitialized `GtkTreeIter` + * + * Initializes @iter with the first iterator in the tree + * (the one at the path "0"). + * + * Returns %FALSE if the tree is empty, %TRUE otherwise. + * + * Returns: %TRUE, if @iter was set + * + * Deprecated: 4.10 + */ +gboolean +gtk_tree_model_get_iter_first (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + GtkTreePath *path; + gboolean retval; + + g_return_val_if_fail (GTK_IS_TREE_MODEL (tree_model), FALSE); + g_return_val_if_fail (iter != NULL, FALSE); + + path = gtk_tree_path_new_first (); + retval = gtk_tree_model_get_iter (tree_model, iter, path); + gtk_tree_path_free (path); + + return retval; +} + +/** + * gtk_tree_model_get_path: + * @tree_model: a `GtkTreeModel` + * @iter: the `GtkTreeIter` + * + * Returns a newly-created `GtkTreePath` referenced by @iter. + * + * This path should be freed with gtk_tree_path_free(). + * + * Returns: a newly-created `GtkTreePath` + * + * Deprecated: 4.10 + */ +GtkTreePath * +gtk_tree_model_get_path (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + GtkTreeModelIface *iface; + + g_return_val_if_fail (GTK_IS_TREE_MODEL (tree_model), NULL); + g_return_val_if_fail (iter != NULL, NULL); + + iface = GTK_TREE_MODEL_GET_IFACE (tree_model); + g_return_val_if_fail (iface->get_path != NULL, NULL); + + return (* iface->get_path) (tree_model, iter); +} + +/** + * gtk_tree_model_get_value: + * @tree_model: a `GtkTreeModel` + * @iter: the `GtkTreeIter` + * @column: the column to lookup the value at + * @value: (out) (transfer none): an empty `GValue` to set + * + * Initializes and sets @value to that at @column. + * + * When done with @value, g_value_unset() needs to be called + * to free any allocated memory. + * + * Deprecated: 4.10 + */ +void +gtk_tree_model_get_value (GtkTreeModel *tree_model, + GtkTreeIter *iter, + int column, + GValue *value) +{ + GtkTreeModelIface *iface; + + g_return_if_fail (GTK_IS_TREE_MODEL (tree_model)); + g_return_if_fail (iter != NULL); + g_return_if_fail (value != NULL); + + iface = GTK_TREE_MODEL_GET_IFACE (tree_model); + g_return_if_fail (iface->get_value != NULL); + + (* iface->get_value) (tree_model, iter, column, value); +} + +/** + * gtk_tree_model_iter_next: + * @tree_model: a `GtkTreeModel` + * @iter: (in): the `GtkTreeIter` + * + * Sets @iter to point to the node following it at the current level. + * + * If there is no next @iter, %FALSE is returned and @iter is set + * to be invalid. + * + * Returns: %TRUE if @iter has been changed to the next node + * + * Deprecated: 4.10 + */ +gboolean +gtk_tree_model_iter_next (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + GtkTreeModelIface *iface; + + g_return_val_if_fail (GTK_IS_TREE_MODEL (tree_model), FALSE); + g_return_val_if_fail (iter != NULL, FALSE); + + iface = GTK_TREE_MODEL_GET_IFACE (tree_model); + g_return_val_if_fail (iface->iter_next != NULL, FALSE); + + return (* iface->iter_next) (tree_model, iter); +} + +static gboolean +gtk_tree_model_iter_previous_default (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + gboolean retval; + GtkTreePath *path; + + path = gtk_tree_model_get_path (tree_model, iter); + if (path == NULL) + return FALSE; + + retval = gtk_tree_path_prev (path) && + gtk_tree_model_get_iter (tree_model, iter, path); + if (retval == FALSE) + iter->stamp = 0; + + gtk_tree_path_free (path); + + return retval; +} + +/** + * gtk_tree_model_iter_previous: + * @tree_model: a `GtkTreeModel` + * @iter: (in): the `GtkTreeIter` + * + * Sets @iter to point to the previous node at the current level. + * + * If there is no previous @iter, %FALSE is returned and @iter is + * set to be invalid. + * + * Returns: %TRUE if @iter has been changed to the previous node + * + * Deprecated: 4.10 + */ +gboolean +gtk_tree_model_iter_previous (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + gboolean retval; + GtkTreeModelIface *iface; + + g_return_val_if_fail (GTK_IS_TREE_MODEL (tree_model), FALSE); + g_return_val_if_fail (iter != NULL, FALSE); + + iface = GTK_TREE_MODEL_GET_IFACE (tree_model); + + if (iface->iter_previous) + retval = (* iface->iter_previous) (tree_model, iter); + else + retval = gtk_tree_model_iter_previous_default (tree_model, iter); + + return retval; +} + +/** + * gtk_tree_model_iter_children: + * @tree_model: a `GtkTreeModel` + * @iter: (out): the new `GtkTreeIter` to be set to the child + * @parent: (nullable): the `GtkTreeIter` + * + * Sets @iter to point to the first child of @parent. + * + * If @parent has no children, %FALSE is returned and @iter is + * set to be invalid. @parent will remain a valid node after this + * function has been called. + * + * If @parent is %NULL returns the first node, equivalent to + * `gtk_tree_model_get_iter_first (tree_model, iter);` + * + * Returns: %TRUE, if @iter has been set to the first child + * + * Deprecated: 4.10 + */ +gboolean +gtk_tree_model_iter_children (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent) +{ + GtkTreeModelIface *iface; + + g_return_val_if_fail (GTK_IS_TREE_MODEL (tree_model), FALSE); + g_return_val_if_fail (iter != NULL, FALSE); + + iface = GTK_TREE_MODEL_GET_IFACE (tree_model); + g_return_val_if_fail (iface->iter_children != NULL, FALSE); + + INITIALIZE_TREE_ITER (iter); + + return (* iface->iter_children) (tree_model, iter, parent); +} + +/** + * gtk_tree_model_iter_has_child: + * @tree_model: a `GtkTreeModel` + * @iter: the `GtkTreeIter` to test for children + * + * Returns %TRUE if @iter has children, %FALSE otherwise. + * + * Returns: %TRUE if @iter has children + * + * Deprecated: 4.10 + */ +gboolean +gtk_tree_model_iter_has_child (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + GtkTreeModelIface *iface; + + g_return_val_if_fail (GTK_IS_TREE_MODEL (tree_model), FALSE); + g_return_val_if_fail (iter != NULL, FALSE); + + iface = GTK_TREE_MODEL_GET_IFACE (tree_model); + g_return_val_if_fail (iface->iter_has_child != NULL, FALSE); + + return (* iface->iter_has_child) (tree_model, iter); +} + +/** + * gtk_tree_model_iter_n_children: + * @tree_model: a `GtkTreeModel` + * @iter: (nullable): the `GtkTreeIter` + * + * Returns the number of children that @iter has. + * + * As a special case, if @iter is %NULL, then the number + * of toplevel nodes is returned. + * + * Returns: the number of children of @iter + * + * Deprecated: 4.10 + */ +int +gtk_tree_model_iter_n_children (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + GtkTreeModelIface *iface; + + g_return_val_if_fail (GTK_IS_TREE_MODEL (tree_model), 0); + + iface = GTK_TREE_MODEL_GET_IFACE (tree_model); + g_return_val_if_fail (iface->iter_n_children != NULL, 0); + + return (* iface->iter_n_children) (tree_model, iter); +} + +/** + * gtk_tree_model_iter_nth_child: + * @tree_model: a `GtkTreeModel` + * @iter: (out): the `GtkTreeIter` to set to the nth child + * @parent: (nullable): the `GtkTreeIter` to get the child from + * @n: the index of the desired child + * + * Sets @iter to be the child of @parent, using the given index. + * + * The first index is 0. If @n is too big, or @parent has no children, + * @iter is set to an invalid iterator and %FALSE is returned. @parent + * will remain a valid node after this function has been called. As a + * special case, if @parent is %NULL, then the @n-th root node + * is set. + * + * Returns: %TRUE, if @parent has an @n-th child + * + * Deprecated: 4.10 + */ +gboolean +gtk_tree_model_iter_nth_child (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent, + int n) +{ + GtkTreeModelIface *iface; + + g_return_val_if_fail (GTK_IS_TREE_MODEL (tree_model), FALSE); + g_return_val_if_fail (iter != NULL, FALSE); + g_return_val_if_fail (n >= 0, FALSE); + + iface = GTK_TREE_MODEL_GET_IFACE (tree_model); + g_return_val_if_fail (iface->iter_nth_child != NULL, FALSE); + + INITIALIZE_TREE_ITER (iter); + + return (* iface->iter_nth_child) (tree_model, iter, parent, n); +} + +/** + * gtk_tree_model_iter_parent: + * @tree_model: a `GtkTreeModel` + * @iter: (out): the new `GtkTreeIter` to set to the parent + * @child: the `GtkTreeIter` + * + * Sets @iter to be the parent of @child. + * + * If @child is at the toplevel, and doesn’t have a parent, then + * @iter is set to an invalid iterator and %FALSE is returned. + * @child will remain a valid node after this function has been + * called. + * + * @iter will be initialized before the lookup is performed, so @child + * and @iter cannot point to the same memory location. + * + * Returns: %TRUE, if @iter is set to the parent of @child + * + * Deprecated: 4.10 + */ +gboolean +gtk_tree_model_iter_parent (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *child) +{ + GtkTreeModelIface *iface; + + g_return_val_if_fail (GTK_IS_TREE_MODEL (tree_model), FALSE); + g_return_val_if_fail (iter != NULL, FALSE); + g_return_val_if_fail (child != NULL, FALSE); + + iface = GTK_TREE_MODEL_GET_IFACE (tree_model); + g_return_val_if_fail (iface->iter_parent != NULL, FALSE); + + INITIALIZE_TREE_ITER (iter); + + return (* iface->iter_parent) (tree_model, iter, child); +} + +/** + * gtk_tree_model_ref_node: + * @tree_model: a `GtkTreeModel` + * @iter: the `GtkTreeIter` + * + * Lets the tree ref the node. + * + * This is an optional method for models to implement. + * To be more specific, models may ignore this call as it exists + * primarily for performance reasons. + * + * This function is primarily meant as a way for views to let + * caching models know when nodes are being displayed (and hence, + * whether or not to cache that node). Being displayed means a node + * is in an expanded branch, regardless of whether the node is currently + * visible in the viewport. For example, a file-system based model + * would not want to keep the entire file-hierarchy in memory, + * just the sections that are currently being displayed by + * every current view. + * + * A model should be expected to be able to get an iter independent + * of its reffed state. + * + * Deprecated: 4.10 + */ +void +gtk_tree_model_ref_node (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + GtkTreeModelIface *iface; + + g_return_if_fail (GTK_IS_TREE_MODEL (tree_model)); + + iface = GTK_TREE_MODEL_GET_IFACE (tree_model); + if (iface->ref_node) + (* iface->ref_node) (tree_model, iter); +} + +/** + * gtk_tree_model_unref_node: + * @tree_model: a `GtkTreeModel` + * @iter: the `GtkTreeIter` + * + * Lets the tree unref the node. + * + * This is an optional method for models to implement. + * To be more specific, models may ignore this call as it exists + * primarily for performance reasons. For more information on what + * this means, see gtk_tree_model_ref_node(). + * + * Please note that nodes that are deleted are not unreffed. + * + * Deprecated: 4.10 + */ +void +gtk_tree_model_unref_node (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + GtkTreeModelIface *iface; + + g_return_if_fail (GTK_IS_TREE_MODEL (tree_model)); + g_return_if_fail (iter != NULL); + + iface = GTK_TREE_MODEL_GET_IFACE (tree_model); + if (iface->unref_node) + (* iface->unref_node) (tree_model, iter); +} + +/** + * gtk_tree_model_get: + * @tree_model: a `GtkTreeModel` + * @iter: a row in @tree_model + * @...: pairs of column number and value return locations, + * terminated by -1 + * + * Gets the value of one or more cells in the row referenced by @iter. + * + * The variable argument list should contain integer column numbers, + * each column number followed by a place to store the value being + * retrieved. The list is terminated by a -1. For example, to get a + * value from column 0 with type %G_TYPE_STRING, you would + * write: `gtk_tree_model_get (model, iter, 0, &place_string_here, -1)`, + * where `place_string_here` is a #gchararray + * to be filled with the string. + * + * Returned values with type %G_TYPE_OBJECT have to be unreferenced, + * values with type %G_TYPE_STRING or %G_TYPE_BOXED have to be freed. + * Other values are passed by value. + * + * Deprecated: 4.10 + */ +void +gtk_tree_model_get (GtkTreeModel *tree_model, + GtkTreeIter *iter, + ...) +{ + va_list var_args; + + g_return_if_fail (GTK_IS_TREE_MODEL (tree_model)); + g_return_if_fail (iter != NULL); + + va_start (var_args, iter); + gtk_tree_model_get_valist (tree_model, iter, var_args); + va_end (var_args); +} + +/** + * gtk_tree_model_get_valist: + * @tree_model: a `GtkTreeModel` + * @iter: a row in @tree_model + * @var_args: va_list of column/return location pairs + * + * Gets the value of one or more cells in the row referenced by @iter. + * + * See [method@Gtk.TreeModel.get], this version takes a va_list + * for language bindings to use. + * + * Deprecated: 4.10 + */ +void +gtk_tree_model_get_valist (GtkTreeModel *tree_model, + GtkTreeIter *iter, + va_list var_args) +{ + int column; + + g_return_if_fail (GTK_IS_TREE_MODEL (tree_model)); + g_return_if_fail (iter != NULL); + + column = va_arg (var_args, int); + + while (column != -1) + { + GValue value = G_VALUE_INIT; + char *error = NULL; + + if (column >= gtk_tree_model_get_n_columns (tree_model)) + { + g_warning ("%s: Invalid column number %d accessed (remember to end your list of columns with a -1)", G_STRLOC, column); + break; + } + + gtk_tree_model_get_value (GTK_TREE_MODEL (tree_model), iter, column, &value); + + G_VALUE_LCOPY (&value, var_args, 0, &error); + if (error) + { + g_warning ("%s: %s", G_STRLOC, error); + g_free (error); + + /* we purposely leak the value here, it might not be + * in a sane state if an error condition occurred + */ + break; + } + + g_value_unset (&value); + + column = va_arg (var_args, int); + } +} + +/** + * gtk_tree_model_row_changed: + * @tree_model: a `GtkTreeModel` + * @path: a `GtkTreePath` pointing to the changed row + * @iter: a valid `GtkTreeIter` pointing to the changed row + * + * Emits the ::row-changed signal on @tree_model. + * + * See [signal@Gtk.TreeModel::row-changed]. + * + * Deprecated: 4.10 + */ +void +gtk_tree_model_row_changed (GtkTreeModel *tree_model, + GtkTreePath *path, + GtkTreeIter *iter) +{ + g_return_if_fail (GTK_IS_TREE_MODEL (tree_model)); + g_return_if_fail (path != NULL); + g_return_if_fail (iter != NULL); + + g_signal_emit (tree_model, tree_model_signals[ROW_CHANGED], 0, path, iter); +} + +/** + * gtk_tree_model_row_inserted: + * @tree_model: a `GtkTreeModel` + * @path: a `GtkTreePath` pointing to the inserted row + * @iter: a valid `GtkTreeIter` pointing to the inserted row + * + * Emits the ::row-inserted signal on @tree_model. + * + * See [signal@Gtk.TreeModel::row-inserted]. + * + * Deprecated: 4.10 + */ +void +gtk_tree_model_row_inserted (GtkTreeModel *tree_model, + GtkTreePath *path, + GtkTreeIter *iter) +{ + g_return_if_fail (GTK_IS_TREE_MODEL (tree_model)); + g_return_if_fail (path != NULL); + g_return_if_fail (iter != NULL); + + g_signal_emit (tree_model, tree_model_signals[ROW_INSERTED], 0, path, iter); +} + +/** + * gtk_tree_model_row_has_child_toggled: + * @tree_model: a `GtkTreeModel` + * @path: a `GtkTreePath` pointing to the changed row + * @iter: a valid `GtkTreeIter` pointing to the changed row + * + * Emits the ::row-has-child-toggled signal on @tree_model. + * + * See [signal@Gtk.TreeModel::row-has-child-toggled]. + * + * This should be called by models after the child + * state of a node changes. + * + * Deprecated: 4.10 + */ +void +gtk_tree_model_row_has_child_toggled (GtkTreeModel *tree_model, + GtkTreePath *path, + GtkTreeIter *iter) +{ + g_return_if_fail (GTK_IS_TREE_MODEL (tree_model)); + g_return_if_fail (path != NULL); + g_return_if_fail (iter != NULL); + + g_signal_emit (tree_model, tree_model_signals[ROW_HAS_CHILD_TOGGLED], 0, path, iter); +} + +/** + * gtk_tree_model_row_deleted: + * @tree_model: a `GtkTreeModel` + * @path: a `GtkTreePath` pointing to the previous location of + * the deleted row + * + * Emits the ::row-deleted signal on @tree_model. + * + * See [signal@Gtk.TreeModel::row-deleted]. + * + * This should be called by models after a row has been removed. + * The location pointed to by @path should be the location that + * the row previously was at. It may not be a valid location anymore. + * + * Nodes that are deleted are not unreffed, this means that any + * outstanding references on the deleted node should not be released. + * + * Deprecated: 4.10 + */ +void +gtk_tree_model_row_deleted (GtkTreeModel *tree_model, + GtkTreePath *path) +{ + g_return_if_fail (GTK_IS_TREE_MODEL (tree_model)); + g_return_if_fail (path != NULL); + + g_signal_emit (tree_model, tree_model_signals[ROW_DELETED], 0, path); +} + +/** + * gtk_tree_model_rows_reordered: (skip) + * @tree_model: a `GtkTreeModel` + * @path: a `GtkTreePath` pointing to the tree node whose children + * have been reordered + * @iter: a valid `GtkTreeIter` pointing to the node whose children + * have been reordered, or %NULL if the depth of @path is 0 + * @new_order: an array of integers mapping the current position of + * each child to its old position before the re-ordering, + * i.e. @new_order`[newpos] = oldpos` + * + * Emits the ::rows-reordered signal on @tree_model. + * + * See [signal@Gtk.TreeModel::rows-reordered]. + * + * This should be called by models when their rows have been + * reordered. + * + * Deprecated: 4.10 + */ +void +gtk_tree_model_rows_reordered (GtkTreeModel *tree_model, + GtkTreePath *path, + GtkTreeIter *iter, + int *new_order) +{ + g_return_if_fail (GTK_IS_TREE_MODEL (tree_model)); + g_return_if_fail (new_order != NULL); + + g_signal_emit (tree_model, tree_model_signals[ROWS_REORDERED], 0, path, iter, new_order); +} + +/** + * gtk_tree_model_rows_reordered_with_length: (rename-to gtk_tree_model_rows_reordered) + * @tree_model: a `GtkTreeModel` + * @path: a `GtkTreePath` pointing to the tree node whose children + * have been reordered + * @iter: (nullable): a valid `GtkTreeIter` pointing to the node + * whose children have been reordered, or %NULL if the depth + * of @path is 0 + * @new_order: (array length=length): an array of integers + * mapping the current position of each child to its old + * position before the re-ordering, + * i.e. @new_order`[newpos] = oldpos` + * @length: length of @new_order array + * + * Emits the ::rows-reordered signal on @tree_model. + * + * See [signal@Gtk.TreeModel::rows-reordered]. + * + * This should be called by models when their rows have been + * reordered. + * + * Deprecated: 4.10 + */ +void +gtk_tree_model_rows_reordered_with_length (GtkTreeModel *tree_model, + GtkTreePath *path, + GtkTreeIter *iter, + int *new_order, + int length) +{ + g_return_if_fail (GTK_IS_TREE_MODEL (tree_model)); + g_return_if_fail (new_order != NULL); + g_return_if_fail (length == gtk_tree_model_iter_n_children (tree_model, iter)); + + g_signal_emit (tree_model, tree_model_signals[ROWS_REORDERED], 0, path, iter, new_order); +} + +static gboolean +gtk_tree_model_foreach_helper (GtkTreeModel *model, + GtkTreeIter *iter, + GtkTreePath *path, + GtkTreeModelForeachFunc func, + gpointer user_data) +{ + gboolean iters_persist; + + iters_persist = gtk_tree_model_get_flags (model) & GTK_TREE_MODEL_ITERS_PERSIST; + + do + { + GtkTreeIter child; + + if ((* func) (model, path, iter, user_data)) + return TRUE; + + if (!iters_persist) + { + if (!gtk_tree_model_get_iter (model, iter, path)) + return TRUE; + } + + if (gtk_tree_model_iter_children (model, &child, iter)) + { + gtk_tree_path_down (path); + if (gtk_tree_model_foreach_helper (model, &child, path, func, user_data)) + return TRUE; + gtk_tree_path_up (path); + } + + gtk_tree_path_next (path); + } + while (gtk_tree_model_iter_next (model, iter)); + + return FALSE; +} + +/** + * gtk_tree_model_foreach: + * @model: a `GtkTreeModel` + * @func: (scope call): a function to be called on each row + * @user_data: (closure): user data to passed to @func + * + * Calls @func on each node in model in a depth-first fashion. + * + * If @func returns %TRUE, then the tree ceases to be walked, + * and gtk_tree_model_foreach() returns. + * + * Deprecated: 4.10 + */ +void +gtk_tree_model_foreach (GtkTreeModel *model, + GtkTreeModelForeachFunc func, + gpointer user_data) +{ + GtkTreePath *path; + GtkTreeIter iter; + + g_return_if_fail (GTK_IS_TREE_MODEL (model)); + g_return_if_fail (func != NULL); + + path = gtk_tree_path_new_first (); + if (!gtk_tree_model_get_iter (model, &iter, path)) + { + gtk_tree_path_free (path); + return; + } + + gtk_tree_model_foreach_helper (model, &iter, path, func, user_data); + gtk_tree_path_free (path); +} + + +/* + * GtkTreeRowReference + */ + +static void gtk_tree_row_reference_unref_path (GtkTreePath *path, + GtkTreeModel *model, + int depth); + + +G_DEFINE_BOXED_TYPE (GtkTreeRowReference, gtk_tree_row_reference, + gtk_tree_row_reference_copy, + gtk_tree_row_reference_free) + +struct _GtkTreeRowReference +{ + GObject *proxy; + GtkTreeModel *model; + GtkTreePath *path; +}; + + +static void +release_row_references (gpointer data) +{ + RowRefList *refs = data; + GSList *tmp_list = NULL; + + tmp_list = refs->list; + while (tmp_list != NULL) + { + GtkTreeRowReference *reference = tmp_list->data; + + if (reference->proxy == (GObject *)reference->model) + reference->model = NULL; + reference->proxy = NULL; + + /* we don't free the reference, users are responsible for that. */ + + tmp_list = tmp_list->next; + } + + g_slist_free (refs->list); + g_free (refs); +} + +static void +gtk_tree_row_ref_inserted (RowRefList *refs, + GtkTreePath *path, + GtkTreeIter *iter) +{ + GSList *tmp_list; + + if (refs == NULL) + return; + + /* This function corrects the path stored in the reference to + * account for an insertion. Note that it's called _after_ the + * insertion with the path to the newly-inserted row. Which means + * that the inserted path is in a different "coordinate system" than + * the old path (e.g. if the inserted path was just before the old + * path, then inserted path and old path will be the same, and old + * path must be moved down one). + */ + + tmp_list = refs->list; + + while (tmp_list != NULL) + { + GtkTreeRowReference *reference = tmp_list->data; + + if (reference->path == NULL) + goto done; + + if (reference->path->depth >= path->depth) + { + int i; + gboolean ancestor = TRUE; + + for (i = 0; i < path->depth - 1; i ++) + { + if (path->indices[i] != reference->path->indices[i]) + { + ancestor = FALSE; + break; + } + } + if (ancestor == FALSE) + goto done; + + if (path->indices[path->depth-1] <= reference->path->indices[path->depth-1]) + reference->path->indices[path->depth-1] += 1; + } + done: + tmp_list = tmp_list->next; + } +} + +static void +gtk_tree_row_ref_deleted (RowRefList *refs, + GtkTreePath *path) +{ + GSList *tmp_list; + + if (refs == NULL) + return; + + /* This function corrects the path stored in the reference to + * account for a deletion. Note that it's called _after_ the + * deletion with the old path of the just-deleted row. Which means + * that the deleted path is the same now-defunct "coordinate system" + * as the path saved in the reference, which is what we want to fix. + */ + + tmp_list = refs->list; + + while (tmp_list != NULL) + { + GtkTreeRowReference *reference = tmp_list->data; + + if (reference->path) + { + int i; + + if (path->depth > reference->path->depth) + goto next; + for (i = 0; i < path->depth - 1; i++) + { + if (path->indices[i] != reference->path->indices[i]) + goto next; + } + + /* We know it affects us. */ + if (path->indices[i] == reference->path->indices[i]) + { + if (reference->path->depth > path->depth) + /* some parent was deleted, trying to unref any node + * between the deleted parent and the node the reference + * is pointing to is bad, as those nodes are already gone. + */ + gtk_tree_row_reference_unref_path (reference->path, reference->model, path->depth - 1); + else + gtk_tree_row_reference_unref_path (reference->path, reference->model, reference->path->depth - 1); + gtk_tree_path_free (reference->path); + reference->path = NULL; + } + else if (path->indices[i] < reference->path->indices[i]) + { + reference->path->indices[path->depth-1]-=1; + } + } + +next: + tmp_list = tmp_list->next; + } +} + +static void +gtk_tree_row_ref_reordered (RowRefList *refs, + GtkTreePath *path, + GtkTreeIter *iter, + int *new_order) +{ + GSList *tmp_list; + int length; + + if (refs == NULL) + return; + + tmp_list = refs->list; + + while (tmp_list != NULL) + { + GtkTreeRowReference *reference = tmp_list->data; + + length = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (reference->model), iter); + + if (length < 2) + return; + + if ((reference->path) && + (gtk_tree_path_is_ancestor (path, reference->path))) + { + int ref_depth = gtk_tree_path_get_depth (reference->path); + int depth = gtk_tree_path_get_depth (path); + + if (ref_depth > depth) + { + int i; + int *indices = gtk_tree_path_get_indices (reference->path); + + for (i = 0; i < length; i++) + { + if (new_order[i] == indices[depth]) + { + indices[depth] = i; + break; + } + } + } + } + + tmp_list = tmp_list->next; + } +} + +/* We do this recursively so that we can unref children nodes + * before their parent + */ +static void +gtk_tree_row_reference_unref_path_helper (GtkTreePath *path, + GtkTreeModel *model, + GtkTreeIter *parent_iter, + int depth, + int current_depth) +{ + GtkTreeIter iter; + + if (depth == current_depth) + return; + + gtk_tree_model_iter_nth_child (model, &iter, parent_iter, path->indices[current_depth]); + gtk_tree_row_reference_unref_path_helper (path, model, &iter, depth, current_depth + 1); + gtk_tree_model_unref_node (model, &iter); +} + +static void +gtk_tree_row_reference_unref_path (GtkTreePath *path, + GtkTreeModel *model, + int depth) +{ + GtkTreeIter iter; + + if (depth <= 0) + return; + + gtk_tree_model_iter_nth_child (model, &iter, NULL, path->indices[0]); + gtk_tree_row_reference_unref_path_helper (path, model, &iter, depth, 1); + gtk_tree_model_unref_node (model, &iter); +} + +/** + * gtk_tree_row_reference_new: + * @model: a `GtkTreeModel` + * @path: a valid `GtkTreePath` to monitor + * + * Creates a row reference based on @path. + * + * This reference will keep pointing to the node pointed to + * by @path, so long as it exists. Any changes that occur on @model are + * propagated, and the path is updated appropriately. If + * @path isn’t a valid path in @model, then %NULL is returned. + * + * Returns: (nullable): a newly allocated `GtkTreeRowReference` + * + * Deprecated: 4.10 + */ +GtkTreeRowReference * +gtk_tree_row_reference_new (GtkTreeModel *model, + GtkTreePath *path) +{ + g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL); + g_return_val_if_fail (path != NULL, NULL); + + /* We use the model itself as the proxy object; and call + * gtk_tree_row_reference_inserted(), etc, in the + * class closure (default handler) marshalers for the signal. + */ + return gtk_tree_row_reference_new_proxy (G_OBJECT (model), model, path); +} + +/** + * gtk_tree_row_reference_new_proxy: + * @proxy: a proxy `GObject` + * @model: a `GtkTreeModel` + * @path: a valid `GtkTreePath` to monitor + * + * You do not need to use this function. + * + * Creates a row reference based on @path. + * + * This reference will keep pointing to the node pointed to + * by @path, so long as it exists. If @path isn’t a valid + * path in @model, then %NULL is returned. However, unlike + * references created with gtk_tree_row_reference_new(), it + * does not listen to the model for changes. The creator of + * the row reference must do this explicitly using + * gtk_tree_row_reference_inserted(), gtk_tree_row_reference_deleted(), + * gtk_tree_row_reference_reordered(). + * + * These functions must be called exactly once per proxy when the + * corresponding signal on the model is emitted. This single call + * updates all row references for that proxy. Since built-in GTK + * objects like `GtkTreeView` already use this mechanism internally, + * using them as the proxy object will produce unpredictable results. + * Further more, passing the same object as @model and @proxy + * doesn’t work for reasons of internal implementation. + * + * This type of row reference is primarily meant by structures that + * need to carefully monitor exactly when a row reference updates + * itself, and is not generally needed by most applications. + * + * Returns: (nullable): a newly allocated `GtkTreeRowReference` + * + * Deprecated: 4.10 + */ +GtkTreeRowReference * +gtk_tree_row_reference_new_proxy (GObject *proxy, + GtkTreeModel *model, + GtkTreePath *path) +{ + GtkTreeRowReference *reference; + RowRefList *refs; + GtkTreeIter parent_iter; + int i; + + g_return_val_if_fail (G_IS_OBJECT (proxy), NULL); + g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL); + g_return_val_if_fail (path != NULL, NULL); + g_return_val_if_fail (path->depth > 0, NULL); + + /* check that the path is valid */ + if (gtk_tree_model_get_iter (model, &parent_iter, path) == FALSE) + return NULL; + + /* Now we want to ref every node */ + gtk_tree_model_iter_nth_child (model, &parent_iter, NULL, path->indices[0]); + gtk_tree_model_ref_node (model, &parent_iter); + + for (i = 1; i < path->depth; i++) + { + GtkTreeIter iter; + gtk_tree_model_iter_nth_child (model, &iter, &parent_iter, path->indices[i]); + gtk_tree_model_ref_node (model, &iter); + parent_iter = iter; + } + + /* Make the row reference */ + reference = g_new (GtkTreeRowReference, 1); + + g_object_ref (proxy); + g_object_ref (model); + reference->proxy = proxy; + reference->model = model; + reference->path = gtk_tree_path_copy (path); + + refs = g_object_get_data (G_OBJECT (proxy), ROW_REF_DATA_STRING); + + if (refs == NULL) + { + refs = g_new (RowRefList, 1); + refs->list = NULL; + + g_object_set_data_full (G_OBJECT (proxy), + I_(ROW_REF_DATA_STRING), + refs, release_row_references); + } + + refs->list = g_slist_prepend (refs->list, reference); + + return reference; +} + +/** + * gtk_tree_row_reference_get_path: + * @reference: a `GtkTreeRowReference` + * + * Returns a path that the row reference currently points to, + * or %NULL if the path pointed to is no longer valid. + * + * Returns: (nullable) (transfer full): a current path + * + * Deprecated: 4.10 + */ +GtkTreePath * +gtk_tree_row_reference_get_path (GtkTreeRowReference *reference) +{ + g_return_val_if_fail (reference != NULL, NULL); + + if (reference->proxy == NULL) + return NULL; + + if (reference->path == NULL) + return NULL; + + return gtk_tree_path_copy (reference->path); +} + +/** + * gtk_tree_row_reference_get_model: + * @reference: a `GtkTreeRowReference` + * + * Returns the model that the row reference is monitoring. + * + * Returns: (transfer none): the model + * + * Deprecated: 4.10 + */ +GtkTreeModel * +gtk_tree_row_reference_get_model (GtkTreeRowReference *reference) +{ + g_return_val_if_fail (reference != NULL, NULL); + + return reference->model; +} + +/** + * gtk_tree_row_reference_valid: + * @reference: (nullable): a `GtkTreeRowReference` + * + * Returns %TRUE if the @reference is non-%NULL and refers to + * a current valid path. + * + * Returns: %TRUE if @reference points to a valid path + * + * Deprecated: 4.10 + */ +gboolean +gtk_tree_row_reference_valid (GtkTreeRowReference *reference) +{ + if (reference == NULL || reference->path == NULL) + return FALSE; + + return TRUE; +} + + +/** + * gtk_tree_row_reference_copy: + * @reference: a `GtkTreeRowReference` + * + * Copies a `GtkTreeRowReference`. + * + * Returns: a copy of @reference + * + * Deprecated: 4.10 + */ +GtkTreeRowReference * +gtk_tree_row_reference_copy (GtkTreeRowReference *reference) +{ + return gtk_tree_row_reference_new_proxy (reference->proxy, + reference->model, + reference->path); +} + +/** + * gtk_tree_row_reference_free: + * @reference: (nullable): a `GtkTreeRowReference` + * + * Free’s @reference. @reference may be %NULL + * + * Deprecated: 4.10 + */ +void +gtk_tree_row_reference_free (GtkTreeRowReference *reference) +{ + RowRefList *refs; + + if (reference == NULL) + return; + + refs = g_object_get_data (G_OBJECT (reference->proxy), ROW_REF_DATA_STRING); + + if (refs == NULL) + { + g_warning (G_STRLOC": bad row reference, proxy has no outstanding row references"); + return; + } + + refs->list = g_slist_remove (refs->list, reference); + + if (refs->list == NULL) + { + g_object_set_data (G_OBJECT (reference->proxy), + I_(ROW_REF_DATA_STRING), + NULL); + } + + if (reference->path) + { + gtk_tree_row_reference_unref_path (reference->path, reference->model, reference->path->depth); + gtk_tree_path_free (reference->path); + } + + g_object_unref (reference->proxy); + g_object_unref (reference->model); + g_free (reference); +} + +/** + * gtk_tree_row_reference_inserted: + * @proxy: a `GObject` + * @path: the row position that was inserted + * + * Lets a set of row reference created by + * gtk_tree_row_reference_new_proxy() know that the + * model emitted the ::row-inserted signal. + * + * Deprecated: 4.10 + */ +void +gtk_tree_row_reference_inserted (GObject *proxy, + GtkTreePath *path) +{ + g_return_if_fail (G_IS_OBJECT (proxy)); + + gtk_tree_row_ref_inserted ((RowRefList *)g_object_get_data (proxy, ROW_REF_DATA_STRING), path, NULL); +} + +/** + * gtk_tree_row_reference_deleted: + * @proxy: a `GObject` + * @path: the path position that was deleted + * + * Lets a set of row reference created by + * gtk_tree_row_reference_new_proxy() know that the + * model emitted the ::row-deleted signal. + * + * Deprecated: 4.10 + */ +void +gtk_tree_row_reference_deleted (GObject *proxy, + GtkTreePath *path) +{ + g_return_if_fail (G_IS_OBJECT (proxy)); + + gtk_tree_row_ref_deleted ((RowRefList *)g_object_get_data (proxy, ROW_REF_DATA_STRING), path); +} + +/** + * gtk_tree_row_reference_reordered: (skip) + * @proxy: a `GObject` + * @path: the parent path of the reordered signal + * @iter: the iter pointing to the parent of the reordered + * @new_order: (array): the new order of rows + * + * Lets a set of row reference created by + * gtk_tree_row_reference_new_proxy() know that the + * model emitted the ::rows-reordered signal. + * + * Deprecated: 4.10 + */ +void +gtk_tree_row_reference_reordered (GObject *proxy, + GtkTreePath *path, + GtkTreeIter *iter, + int *new_order) +{ + g_return_if_fail (G_IS_OBJECT (proxy)); + + gtk_tree_row_ref_reordered ((RowRefList *)g_object_get_data (proxy, ROW_REF_DATA_STRING), path, iter, new_order); +} diff --git a/gtk/deprecated/gtktreemodel.h b/gtk/deprecated/gtktreemodel.h new file mode 100644 index 0000000000..0cfd928a3c --- /dev/null +++ b/gtk/deprecated/gtktreemodel.h @@ -0,0 +1,413 @@ +/* gtktreemodel.h + * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#ifndef __GTK_TREE_MODEL_H__ +#define __GTK_TREE_MODEL_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_TREE_MODEL (gtk_tree_model_get_type ()) +#define GTK_TREE_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_TREE_MODEL, GtkTreeModel)) +#define GTK_IS_TREE_MODEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_TREE_MODEL)) +#define GTK_TREE_MODEL_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GTK_TYPE_TREE_MODEL, GtkTreeModelIface)) + +#define GTK_TYPE_TREE_ITER (gtk_tree_iter_get_type ()) +#define GTK_TYPE_TREE_PATH (gtk_tree_path_get_type ()) +#define GTK_TYPE_TREE_ROW_REFERENCE (gtk_tree_row_reference_get_type ()) + +typedef struct _GtkTreeIter GtkTreeIter; +typedef struct _GtkTreePath GtkTreePath; +typedef struct _GtkTreeRowReference GtkTreeRowReference; +typedef struct _GtkTreeModel GtkTreeModel; /* Dummy typedef */ +typedef struct _GtkTreeModelIface GtkTreeModelIface; + +/** + * GtkTreeModelForeachFunc: + * @model: the `GtkTreeModel` being iterated + * @path: the current `GtkTreePath` + * @iter: the current `GtkTreeIter` + * @data: (closure): The user data passed to gtk_tree_model_foreach() + * + * Type of the callback passed to gtk_tree_model_foreach() to + * iterate over the rows in a tree model. + * + * Returns: %TRUE to stop iterating, %FALSE to continue + * + */ +typedef gboolean (* GtkTreeModelForeachFunc) (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data); + +/** + * GtkTreeModelFlags: + * @GTK_TREE_MODEL_ITERS_PERSIST: iterators survive all signals + * emitted by the tree + * @GTK_TREE_MODEL_LIST_ONLY: the model is a list only, and never + * has children + * + * These flags indicate various properties of a `GtkTreeModel`. + * + * They are returned by [method@Gtk.TreeModel.get_flags], and must be + * static for the lifetime of the object. A more complete description + * of %GTK_TREE_MODEL_ITERS_PERSIST can be found in the overview of + * this section. + */ +typedef enum +{ + GTK_TREE_MODEL_ITERS_PERSIST = 1 << 0, + GTK_TREE_MODEL_LIST_ONLY = 1 << 1 +} GtkTreeModelFlags; + +/** + * GtkTreeIter: + * @stamp: a unique stamp to catch invalid iterators + * @user_data: model-specific data + * @user_data2: model-specific data + * @user_data3: model-specific data + * + * The `GtkTreeIter` is the primary structure + * for accessing a `GtkTreeModel`. Models are expected to put a unique + * integer in the @stamp member, and put + * model-specific data in the three @user_data + * members. + */ +struct _GtkTreeIter +{ + int stamp; + gpointer user_data; + gpointer user_data2; + gpointer user_data3; +}; + +/** + * GtkTreeModelIface: + * @row_changed: Signal emitted when a row in the model has changed. + * @row_inserted: Signal emitted when a new row has been inserted in + * the model. + * @row_has_child_toggled: Signal emitted when a row has gotten the + * first child row or lost its last child row. + * @row_deleted: Signal emitted when a row has been deleted. + * @rows_reordered: Signal emitted when the children of a node in the + * GtkTreeModel have been reordered. + * @get_flags: Get `GtkTreeModelFlags` supported by this interface. + * @get_n_columns: Get the number of columns supported by the model. + * @get_column_type: Get the type of the column. + * @get_iter: Sets iter to a valid iterator pointing to path. + * @get_path: Gets a newly-created `GtkTreePath` referenced by iter. + * @get_value: Initializes and sets value to that at column. + * @iter_next: Sets iter to point to the node following it at the + * current level. + * @iter_previous: Sets iter to point to the previous node at the + * current level. + * @iter_children: Sets iter to point to the first child of parent. + * @iter_has_child: %TRUE if iter has children, %FALSE otherwise. + * @iter_n_children: Gets the number of children that iter has. + * @iter_nth_child: Sets iter to be the child of parent, using the + * given index. + * @iter_parent: Sets iter to be the parent of child. + * @ref_node: Lets the tree ref the node. + * @unref_node: Lets the tree unref the node. + */ +struct _GtkTreeModelIface +{ + /*< private >*/ + GTypeInterface g_iface; + + /*< public >*/ + + /* Signals */ + void (* row_changed) (GtkTreeModel *tree_model, + GtkTreePath *path, + GtkTreeIter *iter); + void (* row_inserted) (GtkTreeModel *tree_model, + GtkTreePath *path, + GtkTreeIter *iter); + void (* row_has_child_toggled) (GtkTreeModel *tree_model, + GtkTreePath *path, + GtkTreeIter *iter); + void (* row_deleted) (GtkTreeModel *tree_model, + GtkTreePath *path); + void (* rows_reordered) (GtkTreeModel *tree_model, + GtkTreePath *path, + GtkTreeIter *iter, + int *new_order); + + /* Virtual Table */ + GtkTreeModelFlags (* get_flags) (GtkTreeModel *tree_model); + + int (* get_n_columns) (GtkTreeModel *tree_model); + GType (* get_column_type) (GtkTreeModel *tree_model, + int index_); + gboolean (* get_iter) (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreePath *path); + GtkTreePath *(* get_path) (GtkTreeModel *tree_model, + GtkTreeIter *iter); + void (* get_value) (GtkTreeModel *tree_model, + GtkTreeIter *iter, + int column, + GValue *value); + gboolean (* iter_next) (GtkTreeModel *tree_model, + GtkTreeIter *iter); + gboolean (* iter_previous) (GtkTreeModel *tree_model, + GtkTreeIter *iter); + gboolean (* iter_children) (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent); + gboolean (* iter_has_child) (GtkTreeModel *tree_model, + GtkTreeIter *iter); + int (* iter_n_children) (GtkTreeModel *tree_model, + GtkTreeIter *iter); + gboolean (* iter_nth_child) (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent, + int n); + gboolean (* iter_parent) (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *child); + void (* ref_node) (GtkTreeModel *tree_model, + GtkTreeIter *iter); + void (* unref_node) (GtkTreeModel *tree_model, + GtkTreeIter *iter); +}; + + +/* GtkTreePath operations */ +GDK_DEPRECATED_IN_4_10 +GtkTreePath *gtk_tree_path_new (void); +GDK_DEPRECATED_IN_4_10 +GtkTreePath *gtk_tree_path_new_from_string (const char *path); +GDK_DEPRECATED_IN_4_10 +GtkTreePath *gtk_tree_path_new_from_indices (int first_index, + ...); +GDK_DEPRECATED_IN_4_10 +GtkTreePath *gtk_tree_path_new_from_indicesv (int *indices, + gsize length); +GDK_DEPRECATED_IN_4_10 +char *gtk_tree_path_to_string (GtkTreePath *path); +GDK_DEPRECATED_IN_4_10 +GtkTreePath *gtk_tree_path_new_first (void); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_path_append_index (GtkTreePath *path, + int index_); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_path_prepend_index (GtkTreePath *path, + int index_); +GDK_DEPRECATED_IN_4_10 +int gtk_tree_path_get_depth (GtkTreePath *path); +GDK_DEPRECATED_IN_4_10 +int *gtk_tree_path_get_indices (GtkTreePath *path); + +GDK_DEPRECATED_IN_4_10 +int *gtk_tree_path_get_indices_with_depth (GtkTreePath *path, + int *depth); + +GDK_DEPRECATED_IN_4_10 +void gtk_tree_path_free (GtkTreePath *path); +GDK_DEPRECATED_IN_4_10 +GtkTreePath *gtk_tree_path_copy (const GtkTreePath *path); +GDK_AVAILABLE_IN_ALL +GType gtk_tree_path_get_type (void) G_GNUC_CONST; +GDK_DEPRECATED_IN_4_10 +int gtk_tree_path_compare (const GtkTreePath *a, + const GtkTreePath *b); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_path_next (GtkTreePath *path); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_path_prev (GtkTreePath *path); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_path_up (GtkTreePath *path); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_path_down (GtkTreePath *path); + +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_path_is_ancestor (GtkTreePath *path, + GtkTreePath *descendant); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_path_is_descendant (GtkTreePath *path, + GtkTreePath *ancestor); + +/** + * GtkTreeRowReference: + * + * A GtkTreeRowReference tracks model changes so that it always refers to the + * same row (a `GtkTreePath` refers to a position, not a fixed row). Create a + * new GtkTreeRowReference with gtk_tree_row_reference_new(). + */ + +GDK_AVAILABLE_IN_ALL +GType gtk_tree_row_reference_get_type (void) G_GNUC_CONST; +GDK_DEPRECATED_IN_4_10 +GtkTreeRowReference *gtk_tree_row_reference_new (GtkTreeModel *model, + GtkTreePath *path); +GDK_DEPRECATED_IN_4_10 +GtkTreeRowReference *gtk_tree_row_reference_new_proxy (GObject *proxy, + GtkTreeModel *model, + GtkTreePath *path); +GDK_DEPRECATED_IN_4_10 +GtkTreePath *gtk_tree_row_reference_get_path (GtkTreeRowReference *reference); +GDK_DEPRECATED_IN_4_10 +GtkTreeModel *gtk_tree_row_reference_get_model (GtkTreeRowReference *reference); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_row_reference_valid (GtkTreeRowReference *reference); +GDK_DEPRECATED_IN_4_10 +GtkTreeRowReference *gtk_tree_row_reference_copy (GtkTreeRowReference *reference); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_row_reference_free (GtkTreeRowReference *reference); +/* These two functions are only needed if you created the row reference with a + * proxy object */ +GDK_DEPRECATED_IN_4_10 +void gtk_tree_row_reference_inserted (GObject *proxy, + GtkTreePath *path); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_row_reference_deleted (GObject *proxy, + GtkTreePath *path); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_row_reference_reordered (GObject *proxy, + GtkTreePath *path, + GtkTreeIter *iter, + int *new_order); + +/* GtkTreeIter operations */ +GDK_DEPRECATED_IN_4_10 +GtkTreeIter * gtk_tree_iter_copy (GtkTreeIter *iter); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_iter_free (GtkTreeIter *iter); +GDK_AVAILABLE_IN_ALL +GType gtk_tree_iter_get_type (void) G_GNUC_CONST; + +GDK_AVAILABLE_IN_ALL +GType gtk_tree_model_get_type (void) G_GNUC_CONST; +GDK_DEPRECATED_IN_4_10 +GtkTreeModelFlags gtk_tree_model_get_flags (GtkTreeModel *tree_model); +GDK_DEPRECATED_IN_4_10 +int gtk_tree_model_get_n_columns (GtkTreeModel *tree_model); +GDK_DEPRECATED_IN_4_10 +GType gtk_tree_model_get_column_type (GtkTreeModel *tree_model, + int index_); + + +/* Iterator movement */ +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_model_get_iter (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreePath *path); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_model_get_iter_from_string (GtkTreeModel *tree_model, + GtkTreeIter *iter, + const char *path_string); +GDK_DEPRECATED_IN_4_10 +char * gtk_tree_model_get_string_from_iter (GtkTreeModel *tree_model, + GtkTreeIter *iter); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_model_get_iter_first (GtkTreeModel *tree_model, + GtkTreeIter *iter); +GDK_DEPRECATED_IN_4_10 +GtkTreePath * gtk_tree_model_get_path (GtkTreeModel *tree_model, + GtkTreeIter *iter); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_model_get_value (GtkTreeModel *tree_model, + GtkTreeIter *iter, + int column, + GValue *value); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_model_iter_previous (GtkTreeModel *tree_model, + GtkTreeIter *iter); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_model_iter_next (GtkTreeModel *tree_model, + GtkTreeIter *iter); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_model_iter_children (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_model_iter_has_child (GtkTreeModel *tree_model, + GtkTreeIter *iter); +GDK_DEPRECATED_IN_4_10 +int gtk_tree_model_iter_n_children (GtkTreeModel *tree_model, + GtkTreeIter *iter); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_model_iter_nth_child (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent, + int n); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_model_iter_parent (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *child); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_model_ref_node (GtkTreeModel *tree_model, + GtkTreeIter *iter); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_model_unref_node (GtkTreeModel *tree_model, + GtkTreeIter *iter); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_model_get (GtkTreeModel *tree_model, + GtkTreeIter *iter, + ...); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_model_get_valist (GtkTreeModel *tree_model, + GtkTreeIter *iter, + va_list var_args); + + +GDK_DEPRECATED_IN_4_10 +void gtk_tree_model_foreach (GtkTreeModel *model, + GtkTreeModelForeachFunc func, + gpointer user_data); + +/* Signals */ +GDK_DEPRECATED_IN_4_10 +void gtk_tree_model_row_changed (GtkTreeModel *tree_model, + GtkTreePath *path, + GtkTreeIter *iter); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_model_row_inserted (GtkTreeModel *tree_model, + GtkTreePath *path, + GtkTreeIter *iter); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_model_row_has_child_toggled (GtkTreeModel *tree_model, + GtkTreePath *path, + GtkTreeIter *iter); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_model_row_deleted (GtkTreeModel *tree_model, + GtkTreePath *path); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_model_rows_reordered (GtkTreeModel *tree_model, + GtkTreePath *path, + GtkTreeIter *iter, + int *new_order); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_model_rows_reordered_with_length (GtkTreeModel *tree_model, + GtkTreePath *path, + GtkTreeIter *iter, + int *new_order, + int length); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkTreeModel, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkTreeIter, gtk_tree_iter_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkTreePath, gtk_tree_path_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkTreeRowReference, gtk_tree_row_reference_free) + +G_END_DECLS + +#endif /* __GTK_TREE_MODEL_H__ */ diff --git a/gtk/deprecated/gtktreemodelfilter.c b/gtk/deprecated/gtktreemodelfilter.c new file mode 100644 index 0000000000..0121c13b48 --- /dev/null +++ b/gtk/deprecated/gtktreemodelfilter.c @@ -0,0 +1,4284 @@ +/* gtktreemodelfilter.c + * Copyright (C) 2000,2001 Red Hat, Inc., Jonathan Blandford + * Copyright (C) 2001-2003 Kristian Rietveld + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#include "config.h" +#include "gtktreemodelfilter.h" +#include "gtktreednd.h" +#include "gtkprivate.h" +#include + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + +/** + * GtkTreeModelFilter: + * + * A `GtkTreeModel` which hides parts of an underlying tree model + * + * A `GtkTreeModelFilter` is a tree model which wraps another tree model, + * and can do the following things: + * + * - Filter specific rows, based on data from a “visible column”, a column + * storing booleans indicating whether the row should be filtered or not, + * or based on the return value of a “visible function”, which gets a + * model, iter and user_data and returns a boolean indicating whether the + * row should be filtered or not. + * + * - Modify the “appearance” of the model, using a modify function. + * This is extremely powerful and allows for just changing some + * values and also for creating a completely different model based + * on the given child model. + * + * - Set a different root node, also known as a “virtual root”. You can pass + * in a `GtkTreePath` indicating the root node for the filter at construction + * time. + * + * The basic API is similar to `GtkTreeModelSort`. For an example on its usage, + * see the section on `GtkTreeModelSort`. + * + * When using `GtkTreeModelFilter`, it is important to realize that + * `GtkTreeModelFilter` maintains an internal cache of all nodes which are + * visible in its clients. The cache is likely to be a subtree of the tree + * exposed by the child model. `GtkTreeModelFilter` will not cache the entire + * child model when unnecessary to not compromise the caching mechanism + * that is exposed by the reference counting scheme. If the child model + * implements reference counting, unnecessary signals may not be emitted + * because of reference counting rule 3, see the `GtkTreeModel` + * documentation. (Note that e.g. `GtkTreeStore` does not implement + * reference counting and will always emit all signals, even when + * the receiving node is not visible). + * + * Because of this, limitations for possible visible functions do apply. + * In general, visible functions should only use data or properties from + * the node for which the visibility state must be determined, its siblings + * or its parents. Usually, having a dependency on the state of any child + * node is not possible, unless references are taken on these explicitly. + * When no such reference exists, no signals may be received for these child + * nodes (see reference counting rule number 3 in the `GtkTreeModel` section). + * + * Determining the visibility state of a given node based on the state + * of its child nodes is a frequently occurring use case. Therefore, + * `GtkTreeModelFilter` explicitly supports this. For example, when a node + * does not have any children, you might not want the node to be visible. + * As soon as the first row is added to the node’s child level (or the + * last row removed), the node’s visibility should be updated. + * + * This introduces a dependency from the node on its child nodes. In order + * to accommodate this, `GtkTreeModelFilter` must make sure the necessary + * signals are received from the child model. This is achieved by building, + * for all nodes which are exposed as visible nodes to `GtkTreeModelFilter`'s + * clients, the child level (if any) and take a reference on the first node + * in this level. Furthermore, for every row-inserted, row-changed or + * row-deleted signal (also these which were not handled because the node + * was not cached), `GtkTreeModelFilter` will check if the visibility state + * of any parent node has changed. + * + * Beware, however, that this explicit support is limited to these two + * cases. For example, if you want a node to be visible only if two nodes + * in a child’s child level (2 levels deeper) are visible, you are on your + * own. In this case, either rely on `GtkTreeStore` to emit all signals + * because it does not implement reference counting, or for models that + * do implement reference counting, obtain references on these child levels + * yourself. + */ + +/* Notes on this implementation of GtkTreeModelFilter + * ================================================== + * + * Warnings + * -------- + * + * In this code there is a potential for confusion as to whether an iter, + * path or value refers to the GtkTreeModelFilter model, or to the child + * model that has been set. As a convention, variables referencing the + * child model will have a c_ prefix before them (ie. c_iter, c_value, + * c_path). In case the c_ prefixed names are already in use, an f_ + * prefix is used. Conversion of iterators and paths between + * GtkTreeModelFilter and the child model is done through the various + * gtk_tree_model_filter_convert_* functions. + * + * Even though the GtkTreeModelSort and GtkTreeModelFilter have very + * similar data structures, many assumptions made in the GtkTreeModelSort + * code do *not* apply in the GtkTreeModelFilter case. Reference counting + * in particular is more complicated in GtkTreeModelFilter, because + * we explicitly support reliance on the state of a node’s children as + * outlined in the public API documentation. Because of these differences, + * you are strongly recommended to first read through these notes before + * making any modification to the code. + * + * Iterator format + * --------------- + * + * The iterator format of iterators handed out by GtkTreeModelFilter is + * as follows: + * + * iter->stamp = filter->priv->stamp + * iter->user_data = FilterLevel + * iter->user_data2 = FilterElt + * + * Internal data structure + * ----------------------- + * + * Using FilterLevel and FilterElt, GtkTreeModelFilter maintains a “cache” + * of the mapping from GtkTreeModelFilter nodes to nodes in the child model. + * This is to avoid re-creating a level each time (which involves computing + * visibility for each node in that level) an operation is requested on + * GtkTreeModelFilter, such as get iter, get path and get value. + * + * A FilterElt corresponds to a single node. The FilterElt can either be + * visible or invisible in the model that is exposed to the clients of this + * GtkTreeModelFilter. The visibility state is stored in the “visible_siter” + * field, which is NULL when the node is not visible. The FilterLevel keeps + * a reference to the parent FilterElt and its FilterLevel (if any). The + * FilterElt can have a “children” pointer set, which points at a child + * level (a sub level). + * + * In a FilterLevel, two separate GSequences are maintained. One contains + * all nodes of this FilterLevel, regardless of the visibility state of + * the node. Another contains only visible nodes. A visible FilterElt + * is thus present in both the full and the visible GSequence. The + * GSequence allows for fast access, addition and removal of nodes. + * + * It is important to recognize the two different mappings that play + * a part in this code: + * I. The mapping from the client to this model. The order in which + * nodes are stored in the *visible* GSequence is the order in + * which the nodes are exposed to clients of the GtkTreeModelFilter. + * II. The mapping from this model to its child model. Each FilterElt + * contains an “offset” field which is the offset of the + * corresponding node in the child model. + * + * Throughout the code, two kinds of paths relative to the GtkTreeModelFilter + * (those generated from the sequence positions) are used. There are paths + * which take non-visible nodes into account (generated from the full + * sequences) and paths which don’t (generated from the visible sequences). + * Paths which have been generated from the full sequences should only be + * used internally and NEVER be passed along with a signal emisson. + * + * Reference counting + * ------------------ + * + * GtkTreeModelFilter forwards all reference and unreference operations + * to the corresponding node in the child model. In addition, + * GtkTreeModelFilter will also add references of its own. The full reference + * count of each node (i.e. all forwarded references and these by the + * filter model) is maintained internally in the “ref_count” fields in + * FilterElt and FilterLevel. Because there is a need to determine whether + * a node should be visible for the client, the reference count of only + * the forwarded references is maintained as well, in the “ext_ref_count” + * fields. + * + * In a few cases, GtkTreeModelFilter takes additional references on + * nodes. The first case is that a reference is taken on the parent + * (if any) of each level. This happens in gtk_tree_model_filter_build_level() + * and the reference is released again in gtk_tree_model_filter_free_level(). + * This ensures that for all references which are taken by the filter + * model, all parent nodes are referenced according to reference counting + * rule 1 in the GtkTreeModel documentation. + * + * A second case is required to support visible functions which depend on + * the state of a node’s children (see the public API documentation for + * GtkTreeModelFilter above). We build the child level of each node that + * could be visible in the client (i.e. the level has an ext_ref_count > 0; + * not the elt, because the elt might be invisible and thus unreferenced + * by the client). For each node that becomes visible, due to insertion or + * changes in visibility state, it is checked whether node has children, if + * so the child level is built. + * + * A reference is taken on the first node of each level so that the child + * model will emit all signals for this level, due to reference counting + * rule 3 in the GtkTreeModel documentation. If due to changes in the level, + * another node becomes the first node (e.g. due to insertion or reordering), + * this reference is transferred from the old to the new first node. + * + * When a level has an *external* reference count of zero (which means that + * none of the nodes in the level is referenced by the clients), the level + * has a “zero ref count” on all its parents. As soon as the level reaches + * an *external* reference count of zero, the zero ref count value is + * incremented by one for all parents of this level. Due to the additional + * references taken by the filter model, it is important to base the + * zero ref count on the external reference count instead of on the full + * reference count of the node. + * + * The zero ref count value aids in determining which portions of the + * cache are possibly unused and could be removed. If a FilterElt has + * a zero ref count of one, then its child level is unused. However, the + * child level can only be removed from the cache if the FilterElt's + * parent level has an external ref count of zero. (Not the parent elt, + * because an invisible parent elt with external ref count == 0 might still + * become visible because of a state change in its child level!). Otherwise, + * monitoring this level is necessary to possibly update the visibility state + * of the parent. This is an important difference from GtkTreeModelSort! + * + * Signals are only required for levels with an external ref count > 0. + * This due to reference counting rule 3, see the GtkTreeModel + * documentation. In the GtkTreeModelFilter we try hard to stick to this + * rule and not emit redundant signals (though redundant emissions of + * row-has-child-toggled could appear frequently; it does happen that + * we simply forward the signal emitted by e.g. GtkTreeStore but also + * emit our own copy). + */ + + +typedef struct _FilterElt FilterElt; +typedef struct _FilterLevel FilterLevel; + +struct _FilterElt +{ + GtkTreeIter iter; + FilterLevel *children; + int offset; + int ref_count; + int ext_ref_count; + int zero_ref_count; + GSequenceIter *visible_siter; /* iter into visible_seq */ +}; + +struct _FilterLevel +{ + GSequence *seq; + GSequence *visible_seq; + int ref_count; + int ext_ref_count; + + FilterElt *parent_elt; + FilterLevel *parent_level; +}; + + +struct _GtkTreeModelFilterPrivate +{ + GtkTreeModel *child_model; + gpointer root; + GtkTreePath *virtual_root; + + int stamp; + guint child_flags; + int zero_ref_count; + int visible_column; + + GtkTreeModelFilterVisibleFunc visible_func; + gpointer visible_data; + GDestroyNotify visible_destroy; + + GType *modify_types; + GtkTreeModelFilterModifyFunc modify_func; + gpointer modify_data; + GDestroyNotify modify_destroy; + int modify_n_columns; + + guint visible_method_set : 1; + guint modify_func_set : 1; + + guint in_row_deleted : 1; + guint virtual_root_deleted : 1; + + /* signal ids */ + gulong changed_id; + gulong inserted_id; + gulong has_child_toggled_id; + gulong deleted_id; + gulong reordered_id; +}; + +/* properties */ +enum +{ + PROP_0, + PROP_CHILD_MODEL, + PROP_VIRTUAL_ROOT +}; + +/* Set this to 0 to disable caching of child iterators. This + * allows for more stringent testing. It is recommended to set this + * to one when refactoring this code and running the unit tests to + * catch more errors. + */ +#if 1 +# define GTK_TREE_MODEL_FILTER_CACHE_CHILD_ITERS(filter) \ + (((GtkTreeModelFilter *)filter)->priv->child_flags & GTK_TREE_MODEL_ITERS_PERSIST) +#else +# define GTK_TREE_MODEL_FILTER_CACHE_CHILD_ITERS(filter) (FALSE) +#endif + +/* Defining this constant enables more assertions, which will be + * helpful when debugging the code. + */ +#undef MODEL_FILTER_DEBUG + +#define FILTER_ELT(filter_elt) ((FilterElt *)filter_elt) +#define FILTER_LEVEL(filter_level) ((FilterLevel *)filter_level) +#define GET_ELT(siter) ((FilterElt*) (siter ? g_sequence_get (siter) : NULL)) + +/* general code (object/interface init, properties, etc) */ +static void gtk_tree_model_filter_tree_model_init (GtkTreeModelIface *iface); +static void gtk_tree_model_filter_drag_source_init (GtkTreeDragSourceIface *iface); +static void gtk_tree_model_filter_finalize (GObject *object); +static void gtk_tree_model_filter_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gtk_tree_model_filter_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); + +/* signal handlers */ +static void gtk_tree_model_filter_row_changed (GtkTreeModel *c_model, + GtkTreePath *c_path, + GtkTreeIter *c_iter, + gpointer data); +static void gtk_tree_model_filter_row_inserted (GtkTreeModel *c_model, + GtkTreePath *c_path, + GtkTreeIter *c_iter, + gpointer data); +static void gtk_tree_model_filter_row_has_child_toggled (GtkTreeModel *c_model, + GtkTreePath *c_path, + GtkTreeIter *c_iter, + gpointer data); +static void gtk_tree_model_filter_row_deleted (GtkTreeModel *c_model, + GtkTreePath *c_path, + gpointer data); +static void gtk_tree_model_filter_rows_reordered (GtkTreeModel *c_model, + GtkTreePath *c_path, + GtkTreeIter *c_iter, + int *new_order, + gpointer data); + +/* GtkTreeModel interface */ +static GtkTreeModelFlags gtk_tree_model_filter_get_flags (GtkTreeModel *model); +static int gtk_tree_model_filter_get_n_columns (GtkTreeModel *model); +static GType gtk_tree_model_filter_get_column_type (GtkTreeModel *model, + int index); +static gboolean gtk_tree_model_filter_get_iter_full (GtkTreeModel *model, + GtkTreeIter *iter, + GtkTreePath *path); +static gboolean gtk_tree_model_filter_get_iter (GtkTreeModel *model, + GtkTreeIter *iter, + GtkTreePath *path); +static GtkTreePath *gtk_tree_model_filter_get_path (GtkTreeModel *model, + GtkTreeIter *iter); +static void gtk_tree_model_filter_get_value (GtkTreeModel *model, + GtkTreeIter *iter, + int column, + GValue *value); +static gboolean gtk_tree_model_filter_iter_next (GtkTreeModel *model, + GtkTreeIter *iter); +static gboolean gtk_tree_model_filter_iter_previous (GtkTreeModel *model, + GtkTreeIter *iter); +static gboolean gtk_tree_model_filter_iter_children (GtkTreeModel *model, + GtkTreeIter *iter, + GtkTreeIter *parent); +static gboolean gtk_tree_model_filter_iter_has_child (GtkTreeModel *model, + GtkTreeIter *iter); +static int gtk_tree_model_filter_iter_n_children (GtkTreeModel *model, + GtkTreeIter *iter); +static gboolean gtk_tree_model_filter_iter_nth_child (GtkTreeModel *model, + GtkTreeIter *iter, + GtkTreeIter *parent, + int n); +static gboolean gtk_tree_model_filter_iter_parent (GtkTreeModel *model, + GtkTreeIter *iter, + GtkTreeIter *child); +static void gtk_tree_model_filter_ref_node (GtkTreeModel *model, + GtkTreeIter *iter); +static void gtk_tree_model_filter_unref_node (GtkTreeModel *model, + GtkTreeIter *iter); + +/* TreeDragSource interface */ +static gboolean gtk_tree_model_filter_row_draggable (GtkTreeDragSource *drag_source, + GtkTreePath *path); +static GdkContentProvider * + gtk_tree_model_filter_drag_data_get (GtkTreeDragSource *drag_source, + GtkTreePath *path); +static gboolean gtk_tree_model_filter_drag_data_delete (GtkTreeDragSource *drag_source, + GtkTreePath *path); + +/* private functions */ +static void gtk_tree_model_filter_build_level (GtkTreeModelFilter *filter, + FilterLevel *parent_level, + FilterElt *parent_elt, + gboolean emit_inserted); + +static void gtk_tree_model_filter_free_level (GtkTreeModelFilter *filter, + FilterLevel *filter_level, + gboolean unref_self, + gboolean unref_parent, + gboolean unref_external); + +static GtkTreePath *gtk_tree_model_filter_elt_get_path (FilterLevel *level, + FilterElt *elt, + GtkTreePath *root); + +static GtkTreePath *gtk_tree_model_filter_add_root (GtkTreePath *src, + GtkTreePath *root); +static GtkTreePath *gtk_tree_model_filter_remove_root (GtkTreePath *src, + GtkTreePath *root); + +static void gtk_tree_model_filter_increment_stamp (GtkTreeModelFilter *filter); + +static void gtk_tree_model_filter_real_modify (GtkTreeModelFilter *self, + GtkTreeModel *child_model, + GtkTreeIter *iter, + GValue *value, + int column); +static gboolean gtk_tree_model_filter_real_visible (GtkTreeModelFilter *filter, + GtkTreeModel *child_model, + GtkTreeIter *child_iter); +static gboolean gtk_tree_model_filter_visible (GtkTreeModelFilter *filter, + GtkTreeIter *child_iter); +static void gtk_tree_model_filter_clear_cache_helper (GtkTreeModelFilter *filter, + FilterLevel *level); + +static void gtk_tree_model_filter_real_ref_node (GtkTreeModel *model, + GtkTreeIter *iter, + gboolean external); +static void gtk_tree_model_filter_real_unref_node (GtkTreeModel *model, + GtkTreeIter *iter, + gboolean external, + gboolean propagate_unref); + +static void gtk_tree_model_filter_set_model (GtkTreeModelFilter *filter, + GtkTreeModel *child_model); +static void gtk_tree_model_filter_ref_path (GtkTreeModelFilter *filter, + GtkTreePath *path); +static void gtk_tree_model_filter_unref_path (GtkTreeModelFilter *filter, + GtkTreePath *path, + int depth); +static void gtk_tree_model_filter_set_root (GtkTreeModelFilter *filter, + GtkTreePath *root); + +static GtkTreePath *gtk_real_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter *filter, + GtkTreePath *child_path, + gboolean build_levels, + gboolean fetch_children); + +static gboolean gtk_tree_model_filter_elt_is_visible_in_target (FilterLevel *level, + FilterElt *elt); + +static FilterElt *gtk_tree_model_filter_insert_elt_in_level (GtkTreeModelFilter *filter, + GtkTreeIter *c_iter, + FilterLevel *level, + int offset, + int *index); +static FilterElt *gtk_tree_model_filter_fetch_child (GtkTreeModelFilter *filter, + FilterLevel *level, + int offset, + int *index); +static void gtk_tree_model_filter_remove_elt_from_level (GtkTreeModelFilter *filter, + FilterLevel *level, + FilterElt *elt); +static void gtk_tree_model_filter_update_children (GtkTreeModelFilter *filter, + FilterLevel *level, + FilterElt *elt); +static void gtk_tree_model_filter_emit_row_inserted_for_path (GtkTreeModelFilter *filter, + GtkTreeModel *c_model, + GtkTreePath *c_path, + GtkTreeIter *c_iter); + + +G_DEFINE_TYPE_WITH_CODE (GtkTreeModelFilter, gtk_tree_model_filter, G_TYPE_OBJECT, + G_ADD_PRIVATE (GtkTreeModelFilter) + G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, + gtk_tree_model_filter_tree_model_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE, + gtk_tree_model_filter_drag_source_init)) + +static void +gtk_tree_model_filter_init (GtkTreeModelFilter *filter) +{ + filter->priv = gtk_tree_model_filter_get_instance_private (filter); + filter->priv->visible_column = -1; + filter->priv->zero_ref_count = 0; + filter->priv->visible_method_set = FALSE; + filter->priv->modify_func_set = FALSE; + filter->priv->in_row_deleted = FALSE; + filter->priv->virtual_root_deleted = FALSE; +} + +static void +gtk_tree_model_filter_class_init (GtkTreeModelFilterClass *filter_class) +{ + GObjectClass *object_class; + + object_class = (GObjectClass *) filter_class; + + object_class->set_property = gtk_tree_model_filter_set_property; + object_class->get_property = gtk_tree_model_filter_get_property; + + object_class->finalize = gtk_tree_model_filter_finalize; + + filter_class->visible = gtk_tree_model_filter_real_visible; + filter_class->modify = gtk_tree_model_filter_real_modify; + + g_object_class_install_property (object_class, + PROP_CHILD_MODEL, + g_param_spec_object ("child-model", NULL, NULL, + GTK_TYPE_TREE_MODEL, + GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, + PROP_VIRTUAL_ROOT, + g_param_spec_boxed ("virtual-root", NULL, NULL, + GTK_TYPE_TREE_PATH, + GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); +} + +static void +gtk_tree_model_filter_tree_model_init (GtkTreeModelIface *iface) +{ + iface->get_flags = gtk_tree_model_filter_get_flags; + iface->get_n_columns = gtk_tree_model_filter_get_n_columns; + iface->get_column_type = gtk_tree_model_filter_get_column_type; + iface->get_iter = gtk_tree_model_filter_get_iter; + iface->get_path = gtk_tree_model_filter_get_path; + iface->get_value = gtk_tree_model_filter_get_value; + iface->iter_next = gtk_tree_model_filter_iter_next; + iface->iter_previous = gtk_tree_model_filter_iter_previous; + iface->iter_children = gtk_tree_model_filter_iter_children; + iface->iter_has_child = gtk_tree_model_filter_iter_has_child; + iface->iter_n_children = gtk_tree_model_filter_iter_n_children; + iface->iter_nth_child = gtk_tree_model_filter_iter_nth_child; + iface->iter_parent = gtk_tree_model_filter_iter_parent; + iface->ref_node = gtk_tree_model_filter_ref_node; + iface->unref_node = gtk_tree_model_filter_unref_node; +} + +static void +gtk_tree_model_filter_drag_source_init (GtkTreeDragSourceIface *iface) +{ + iface->row_draggable = gtk_tree_model_filter_row_draggable; + iface->drag_data_delete = gtk_tree_model_filter_drag_data_delete; + iface->drag_data_get = gtk_tree_model_filter_drag_data_get; +} + + +static void +gtk_tree_model_filter_finalize (GObject *object) +{ + GtkTreeModelFilter *filter = (GtkTreeModelFilter *) object; + + if (filter->priv->virtual_root && !filter->priv->virtual_root_deleted) + { + gtk_tree_model_filter_unref_path (filter, filter->priv->virtual_root, + -1); + filter->priv->virtual_root_deleted = TRUE; + } + + gtk_tree_model_filter_set_model (filter, NULL); + + if (filter->priv->virtual_root) + gtk_tree_path_free (filter->priv->virtual_root); + + if (filter->priv->root) + gtk_tree_model_filter_free_level (filter, filter->priv->root, TRUE, TRUE, FALSE); + + g_free (filter->priv->modify_types); + + if (filter->priv->modify_destroy) + filter->priv->modify_destroy (filter->priv->modify_data); + + if (filter->priv->visible_destroy) + filter->priv->visible_destroy (filter->priv->visible_data); + + /* must chain up */ + G_OBJECT_CLASS (gtk_tree_model_filter_parent_class)->finalize (object); +} + +static void +gtk_tree_model_filter_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (object); + + switch (prop_id) + { + case PROP_CHILD_MODEL: + gtk_tree_model_filter_set_model (filter, g_value_get_object (value)); + break; + case PROP_VIRTUAL_ROOT: + gtk_tree_model_filter_set_root (filter, g_value_get_boxed (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_tree_model_filter_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (object); + + switch (prop_id) + { + case PROP_CHILD_MODEL: + g_value_set_object (value, filter->priv->child_model); + break; + case PROP_VIRTUAL_ROOT: + g_value_set_boxed (value, filter->priv->virtual_root); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +/* helpers */ + +static FilterElt * +filter_elt_new (void) +{ + return g_slice_new (FilterElt); +} + +static void +filter_elt_free (gpointer elt) +{ + g_slice_free (FilterElt, elt); +} + +static int +filter_elt_cmp (gconstpointer a, + gconstpointer b, + gpointer user_data) +{ + const FilterElt *elt_a = a; + const FilterElt *elt_b = b; + + if (elt_a->offset > elt_b->offset) + return +1; + else if (elt_a->offset < elt_b->offset) + return -1; + else + return 0; +} + +static FilterElt * +lookup_elt_with_offset (GSequence *seq, + int offset, + GSequenceIter **ret_siter) +{ + GSequenceIter *siter; + FilterElt dummy; + + dummy.offset = offset; + siter = g_sequence_lookup (seq, &dummy, filter_elt_cmp, NULL); + + if (ret_siter) + *ret_siter = siter; + + return GET_ELT (siter); +} + +static void +increase_offset_iter (gpointer data, + gpointer user_data) +{ + FilterElt *elt = data; + int offset = GPOINTER_TO_INT (user_data); + + if (elt->offset >= offset) + elt->offset++; +} + +static void +decrease_offset_iter (gpointer data, + gpointer user_data) +{ + FilterElt *elt = data; + int offset = GPOINTER_TO_INT (user_data); + + if (elt->offset > offset) + elt->offset--; +} + +static void +gtk_tree_model_filter_build_level (GtkTreeModelFilter *filter, + FilterLevel *parent_level, + FilterElt *parent_elt, + gboolean emit_inserted) +{ + GtkTreeIter iter; + GtkTreeIter first_node; + GtkTreeIter root; + FilterLevel *new_level; + FilterLevel *tmp_level; + FilterElt *tmp_elt; + GtkTreeIter f_iter; + int length = 0; + int i; + gboolean empty = TRUE; + + g_assert (filter->priv->child_model != NULL); + + /* Avoid building a level that already exists */ + if (parent_level) + g_assert (parent_elt->children == NULL); + else + g_assert (filter->priv->root == NULL); + + if (filter->priv->in_row_deleted) + return; + + if (!parent_level) + { + if (filter->priv->virtual_root) + { + if (gtk_tree_model_get_iter (filter->priv->child_model, &root, filter->priv->virtual_root) == FALSE) + return; + length = gtk_tree_model_iter_n_children (filter->priv->child_model, &root); + + if (gtk_tree_model_iter_children (filter->priv->child_model, &iter, &root) == FALSE) + return; + } + else + { + if (!gtk_tree_model_get_iter_first (filter->priv->child_model, &iter)) + return; + length = gtk_tree_model_iter_n_children (filter->priv->child_model, NULL); + } + } + else + { + GtkTreeIter parent_iter; + GtkTreeIter child_parent_iter; + + parent_iter.stamp = filter->priv->stamp; + parent_iter.user_data = parent_level; + parent_iter.user_data2 = parent_elt; + + gtk_tree_model_filter_convert_iter_to_child_iter (filter, + &child_parent_iter, + &parent_iter); + if (gtk_tree_model_iter_children (filter->priv->child_model, &iter, &child_parent_iter) == FALSE) + return; + + /* stamp may have changed */ + gtk_tree_model_filter_convert_iter_to_child_iter (filter, + &child_parent_iter, + &parent_iter); + length = gtk_tree_model_iter_n_children (filter->priv->child_model, &child_parent_iter); + + /* Take a reference on the parent */ + gtk_tree_model_filter_real_ref_node (GTK_TREE_MODEL (filter), + &parent_iter, FALSE); + } + + g_return_if_fail (length > 0); + + new_level = g_new (FilterLevel, 1); + new_level->seq = g_sequence_new (filter_elt_free); + new_level->visible_seq = g_sequence_new (NULL); + new_level->ref_count = 0; + new_level->ext_ref_count = 0; + new_level->parent_elt = parent_elt; + new_level->parent_level = parent_level; + + if (parent_elt) + parent_elt->children = new_level; + else + filter->priv->root = new_level; + + /* increase the count of zero ref_counts */ + tmp_level = parent_level; + tmp_elt = parent_elt; + + while (tmp_level) + { + tmp_elt->zero_ref_count++; + + tmp_elt = tmp_level->parent_elt; + tmp_level = tmp_level->parent_level; + } + if (new_level != filter->priv->root) + filter->priv->zero_ref_count++; + + i = 0; + + first_node = iter; + + do + { + if (gtk_tree_model_filter_visible (filter, &iter)) + { + FilterElt *filter_elt; + + filter_elt = filter_elt_new (); + filter_elt->offset = i; + filter_elt->zero_ref_count = 0; + filter_elt->ref_count = 0; + filter_elt->ext_ref_count = 0; + filter_elt->children = NULL; + filter_elt->visible_siter = NULL; + + if (GTK_TREE_MODEL_FILTER_CACHE_CHILD_ITERS (filter)) + filter_elt->iter = iter; + + g_sequence_append (new_level->seq, filter_elt); + filter_elt->visible_siter = g_sequence_append (new_level->visible_seq, filter_elt); + empty = FALSE; + + if (emit_inserted) + { + GtkTreePath *f_path; + GtkTreeIter children; + + f_iter.stamp = filter->priv->stamp; + f_iter.user_data = new_level; + f_iter.user_data2 = filter_elt; + + f_path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), + &f_iter); + gtk_tree_model_row_inserted (GTK_TREE_MODEL (filter), + f_path, &f_iter); + gtk_tree_path_free (f_path); + + if (gtk_tree_model_iter_children (filter->priv->child_model, + &children, &iter)) + gtk_tree_model_filter_update_children (filter, + new_level, + FILTER_ELT (f_iter.user_data2)); + } + } + i++; + } + while (gtk_tree_model_iter_next (filter->priv->child_model, &iter)); + + /* The level does not contain any visible nodes. However, changes in + * this level might affect the parent node, which can either be visible + * or invisible. Therefore, this level can only be removed again, + * if the parent level has an external reference count of zero. That is, + * if this level changes state, no signals are required in the parent + * level. + */ + if (empty && + (parent_level && parent_level->ext_ref_count == 0)) + { + gtk_tree_model_filter_free_level (filter, new_level, FALSE, TRUE, FALSE); + return; + } + + /* If none of the nodes are visible, we will just pull in the + * first node of the level. + */ + if (empty) + { + FilterElt *filter_elt; + + filter_elt = filter_elt_new (); + filter_elt->offset = 0; + filter_elt->zero_ref_count = 0; + filter_elt->ref_count = 0; + filter_elt->ext_ref_count = 0; + filter_elt->children = NULL; + filter_elt->visible_siter = NULL; + + if (GTK_TREE_MODEL_FILTER_CACHE_CHILD_ITERS (filter)) + filter_elt->iter = first_node; + + g_sequence_append (new_level->seq, filter_elt); + } + + /* Keep a reference on the first node of this level. We need this + * to make sure that we get all signals for this level. + */ + f_iter.stamp = filter->priv->stamp; + f_iter.user_data = new_level; + f_iter.user_data2 = g_sequence_get (g_sequence_get_begin_iter (new_level->seq)); + + gtk_tree_model_filter_real_ref_node (GTK_TREE_MODEL (filter), &f_iter, FALSE); +} + +static void +gtk_tree_model_filter_free_level (GtkTreeModelFilter *filter, + FilterLevel *filter_level, + gboolean unref_self, + gboolean unref_parent, + gboolean unref_external) +{ + GSequenceIter *siter; + GSequenceIter *end_siter; + + g_assert (filter_level); + + end_siter = g_sequence_get_end_iter (filter_level->seq); + for (siter = g_sequence_get_begin_iter (filter_level->seq); + siter != end_siter; + siter = g_sequence_iter_next (siter)) + { + FilterElt *elt = g_sequence_get (siter); + + if (elt->children) + { + /* If we recurse and unref_self == FALSE, then unref_parent + * must also be FALSE (otherwise a still unref a node in this + * level). + */ + gtk_tree_model_filter_free_level (filter, + FILTER_LEVEL (elt->children), + unref_self, + unref_self == FALSE ? FALSE : unref_parent, + unref_external); + } + + if (unref_external) + { + GtkTreeIter f_iter; + + f_iter.stamp = filter->priv->stamp; + f_iter.user_data = filter_level; + f_iter.user_data2 = elt; + + while (elt->ext_ref_count > 0) + gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter), + &f_iter, + TRUE, unref_self); + } + } + + /* Release the reference on the first item. + */ + if (unref_self) + { + GtkTreeIter f_iter; + + f_iter.stamp = filter->priv->stamp; + f_iter.user_data = filter_level; + f_iter.user_data2 = g_sequence_get (g_sequence_get_begin_iter (filter_level->seq)); + + gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter), + &f_iter, FALSE, TRUE); + } + + if (filter_level->ext_ref_count == 0) + { + FilterLevel *parent_level = filter_level->parent_level; + FilterElt *parent_elt = filter_level->parent_elt; + + while (parent_level) + { + parent_elt->zero_ref_count--; + + parent_elt = parent_level->parent_elt; + parent_level = parent_level->parent_level; + } + + if (filter_level != filter->priv->root) + filter->priv->zero_ref_count--; + } + +#ifdef MODEL_FILTER_DEBUG + if (filter_level == filter->priv->root) + g_assert (filter->priv->zero_ref_count == 0); +#endif + + if (filter_level->parent_elt) + { + /* Release reference on parent */ + GtkTreeIter parent_iter; + + parent_iter.stamp = filter->priv->stamp; + parent_iter.user_data = filter_level->parent_level; + parent_iter.user_data2 = filter_level->parent_elt; + + gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter), + &parent_iter, FALSE, unref_parent); + + filter_level->parent_elt->children = NULL; + } + else + filter->priv->root = NULL; + + g_sequence_free (filter_level->seq); + g_sequence_free (filter_level->visible_seq); + g_free (filter_level); +} + +/* prune_level() is like free_level(), however instead of being fully + * freed, the level is pruned to a level with only the first node used + * for monitoring. For now it is only being called from + * gtk_tree_model_filter_remove_elt_from_level(), which is the reason + * this function is lacking a “gboolean unref” argument. + */ +static void +gtk_tree_model_filter_prune_level (GtkTreeModelFilter *filter, + FilterLevel *level) +{ + GSequenceIter *siter; + GSequenceIter *end_siter; + FilterElt *elt; + GtkTreeIter f_iter; + + /* This function is called when the parent of level became invisible. + * All external ref counts of the children need to be dropped. + * All children except the first one can be removed. + */ + + /* Any child levels can be freed */ + end_siter = g_sequence_get_end_iter (level->seq); + for (siter = g_sequence_get_begin_iter (level->seq); + siter != end_siter; + siter = g_sequence_iter_next (siter)) + { + elt = g_sequence_get (siter); + + if (elt->children) + gtk_tree_model_filter_free_level (filter, + FILTER_LEVEL (elt->children), + TRUE, TRUE, TRUE); + } + + /* For the first item, only drop the external references */ + elt = g_sequence_get (g_sequence_get_begin_iter (level->seq)); + + f_iter.stamp = filter->priv->stamp; + f_iter.user_data = level; + f_iter.user_data2 = elt; + + while (elt->ext_ref_count > 0) + gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter), + &f_iter, TRUE, TRUE); + + if (elt->visible_siter) + { + g_sequence_remove (elt->visible_siter); + elt->visible_siter = NULL; + } + + /* Remove the other elts */ + end_siter = g_sequence_get_end_iter (level->seq); + siter = g_sequence_get_begin_iter (level->seq); + siter = g_sequence_iter_next (siter); + for (; siter != end_siter; siter = g_sequence_iter_next (siter)) + { + elt = g_sequence_get (siter); + + f_iter.stamp = filter->priv->stamp; + f_iter.user_data = level; + f_iter.user_data2 = elt; + + while (elt->ext_ref_count > 0) + gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter), + &f_iter, TRUE, TRUE); + /* In this case, we do remove reference counts we've added ourselves, + * since the node will be removed from the data structures. + */ + while (elt->ref_count > 0) + gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter), + &f_iter, FALSE, TRUE); + + if (elt->visible_siter) + { + g_sequence_remove (elt->visible_siter); + elt->visible_siter = NULL; + } + } + + /* Remove [begin + 1, end] */ + siter = g_sequence_get_begin_iter (level->seq); + siter = g_sequence_iter_next (siter); + + g_sequence_remove_range (siter, end_siter); + + /* The level must have reached an ext ref count of zero by now, though + * we only assert on this in debugging mode. + */ +#ifdef MODEL_FILTER_DEBUG + g_assert (level->ext_ref_count == 0); +#endif +} + +static void +gtk_tree_model_filter_level_transfer_first_ref (GtkTreeModelFilter *filter, + FilterLevel *level, + GSequenceIter *from_iter, + GSequenceIter *to_iter) +{ + GtkTreeIter f_iter; + + f_iter.stamp = filter->priv->stamp; + f_iter.user_data = level; + f_iter.user_data2 = g_sequence_get (to_iter); + + gtk_tree_model_filter_real_ref_node (GTK_TREE_MODEL (filter), + &f_iter, FALSE); + + f_iter.stamp = filter->priv->stamp; + f_iter.user_data = level; + f_iter.user_data2 = g_sequence_get (from_iter); + + gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter), + &f_iter, FALSE, TRUE); +} + +static void +gtk_tree_model_filter_level_transfer_first_ref_with_index (GtkTreeModelFilter *filter, + FilterLevel *level, + int from_index, + int to_index) +{ + gtk_tree_model_filter_level_transfer_first_ref (filter, level, + g_sequence_get_iter_at_pos (level->seq, from_index), + g_sequence_get_iter_at_pos (level->seq, to_index)); +} + +/* Creates paths suitable for accessing the child model. */ +static GtkTreePath * +gtk_tree_model_filter_elt_get_path (FilterLevel *level, + FilterElt *elt, + GtkTreePath *root) +{ + FilterLevel *walker = level; + FilterElt *walker2 = elt; + GtkTreePath *path; + GtkTreePath *real_path; + + g_return_val_if_fail (level != NULL, NULL); + g_return_val_if_fail (elt != NULL, NULL); + + path = gtk_tree_path_new (); + + while (walker) + { + gtk_tree_path_prepend_index (path, walker2->offset); + + walker2 = walker->parent_elt; + walker = walker->parent_level; + } + + if (root) + { + real_path = gtk_tree_model_filter_add_root (path, root); + gtk_tree_path_free (path); + return real_path; + } + + return path; +} + +static GtkTreePath * +gtk_tree_model_filter_add_root (GtkTreePath *src, + GtkTreePath *root) +{ + GtkTreePath *retval; + int i; + + retval = gtk_tree_path_copy (root); + + for (i = 0; i < gtk_tree_path_get_depth (src); i++) + gtk_tree_path_append_index (retval, gtk_tree_path_get_indices (src)[i]); + + return retval; +} + +static GtkTreePath * +gtk_tree_model_filter_remove_root (GtkTreePath *src, + GtkTreePath *root) +{ + GtkTreePath *retval; + int i; + int depth; + int *indices; + + if (gtk_tree_path_get_depth (src) <= gtk_tree_path_get_depth (root)) + return NULL; + + depth = gtk_tree_path_get_depth (src); + indices = gtk_tree_path_get_indices (src); + + for (i = 0; i < gtk_tree_path_get_depth (root); i++) + if (indices[i] != gtk_tree_path_get_indices (root)[i]) + return NULL; + + retval = gtk_tree_path_new (); + + for (; i < depth; i++) + gtk_tree_path_append_index (retval, indices[i]); + + return retval; +} + +static void +gtk_tree_model_filter_increment_stamp (GtkTreeModelFilter *filter) +{ + do + { + filter->priv->stamp++; + } + while (filter->priv->stamp == 0); + + gtk_tree_model_filter_clear_cache (filter); +} + +static gboolean +gtk_tree_model_filter_real_visible (GtkTreeModelFilter *filter, + GtkTreeModel *child_model, + GtkTreeIter *child_iter) +{ + if (filter->priv->visible_func) + { + return filter->priv->visible_func (child_model, + child_iter, + filter->priv->visible_data) + ? TRUE : FALSE; + } + else if (filter->priv->visible_column >= 0) + { + GValue val = G_VALUE_INIT; + + gtk_tree_model_get_value (child_model, child_iter, + filter->priv->visible_column, &val); + + if (g_value_get_boolean (&val)) + { + g_value_unset (&val); + return TRUE; + } + + g_value_unset (&val); + return FALSE; + } + + /* no visible function set, so always visible */ + return TRUE; +} + +static gboolean +gtk_tree_model_filter_visible (GtkTreeModelFilter *self, + GtkTreeIter *child_iter) +{ + return GTK_TREE_MODEL_FILTER_GET_CLASS (self)->visible (self, + self->priv->child_model, child_iter); +} + +static void +gtk_tree_model_filter_clear_cache_helper_iter (gpointer data, + gpointer user_data) +{ + GtkTreeModelFilter *filter = user_data; + FilterElt *elt = data; + +#ifdef MODEL_FILTER_DEBUG + g_assert (elt->zero_ref_count >= 0); +#endif + + if (elt->zero_ref_count > 0) + gtk_tree_model_filter_clear_cache_helper (filter, elt->children); +} + +static void +gtk_tree_model_filter_clear_cache_helper (GtkTreeModelFilter *filter, + FilterLevel *level) +{ + g_assert (level); + + g_sequence_foreach (level->seq, gtk_tree_model_filter_clear_cache_helper_iter, filter); + + /* If the level's ext_ref_count is zero, it means the level is not visible + * and can be removed. But, since we support monitoring a child level + * of a parent for changes (these might affect the parent), we will only + * free the level if the parent level also has an external ref + * count of zero. In that case, changes concerning our parent are + * not requested. + * + * The root level is always visible, so an exception holds for levels + * with the root level as parent level: these have to remain cached. + */ + if (level->ext_ref_count == 0 && level != filter->priv->root && + level->parent_level && level->parent_level != filter->priv->root && + level->parent_level->ext_ref_count == 0) + { + gtk_tree_model_filter_free_level (filter, level, TRUE, TRUE, FALSE); + return; + } +} + +static gboolean +gtk_tree_model_filter_elt_is_visible_in_target (FilterLevel *level, + FilterElt *elt) +{ + if (!elt->visible_siter) + return FALSE; + + if (!level->parent_elt) + return TRUE; + + do + { + elt = level->parent_elt; + level = level->parent_level; + + if (elt && !elt->visible_siter) + return FALSE; + } + while (level); + + return TRUE; +} + +/* If a change has occurred in path (inserted, changed or deleted), + * then this function is used to check all its ancestors. An ancestor + * could have changed state as a result and this needs to be propagated + * to the objects monitoring the filter model. + */ +static void +gtk_tree_model_filter_check_ancestors (GtkTreeModelFilter *filter, + GtkTreePath *path) +{ + int i = 0; + int *indices = gtk_tree_path_get_indices (path); + FilterElt *elt; + FilterLevel *level; + GtkTreeIter c_iter, tmp_iter, *root_iter; + + level = FILTER_LEVEL (filter->priv->root); + + if (!level) + return; + + root_iter = NULL; + if (filter->priv->virtual_root && + gtk_tree_model_get_iter (filter->priv->child_model, &tmp_iter, + filter->priv->virtual_root)) + root_iter = &tmp_iter; + gtk_tree_model_iter_nth_child (filter->priv->child_model, &c_iter, + root_iter, + indices[i]); + + while (i < gtk_tree_path_get_depth (path) - 1) + { + gboolean requested_state; + + elt = lookup_elt_with_offset (level->seq, + gtk_tree_path_get_indices (path)[i], NULL); + + requested_state = gtk_tree_model_filter_visible (filter, &c_iter); + + if (!elt) + { + int index; + GtkTreePath *c_path; + + if (requested_state == FALSE) + return; + + /* The elt does not exist in this level (so it is not + * visible), but should now be visible. We emit the + * row-inserted and row-has-child-toggled signals. + */ + elt = gtk_tree_model_filter_insert_elt_in_level (filter, + &c_iter, + level, + indices[i], + &index); + + /* insert_elt_in_level defaults to FALSE */ + elt->visible_siter = g_sequence_insert_sorted (level->visible_seq, + elt, + filter_elt_cmp, NULL); + + c_path = gtk_tree_model_get_path (filter->priv->child_model, + &c_iter); + + gtk_tree_model_filter_emit_row_inserted_for_path (filter, + filter->priv->child_model, + c_path, + &c_iter); + + gtk_tree_path_free (c_path); + + /* We can immediately return, because this node was not visible + * before and its children will be checked for in response to + * the emitted row-has-child-toggled signal. + */ + return; + } + else if (elt->visible_siter) + { + if (!requested_state) + { + /* A node has turned invisible. Remove it from the level + * and emit row-deleted. Since this node is being + * deleted. it makes no sense to look further up the + * chain. + */ + gtk_tree_model_filter_remove_elt_from_level (filter, + level, elt); + return; + } + + /* Otherwise continue up the chain */ + } + else if (!elt->visible_siter) + { + if (requested_state) + { + /* A node is already in the cache, but invisible. This + * is usually a node on which a reference is kept by + * the filter model, or a node fetched on the filter's + * request, and thus not shown. Therefore, we will + * not emit row-inserted for this node. Instead, + * we signal to its parent that a change has occurred. + * + * Exception: root level, in this case, we must emit + * row-inserted. + */ + if (level->parent_level) + { + GtkTreeIter f_iter; + GtkTreePath *f_path; + + elt->visible_siter = g_sequence_insert_sorted (level->visible_seq, elt, + filter_elt_cmp, NULL); + + f_iter.stamp = filter->priv->stamp; + f_iter.user_data = level->parent_level; + f_iter.user_data2 = level->parent_elt; + + f_path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), + &f_iter); + gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (filter), + f_path, &f_iter); + gtk_tree_path_free (f_path); + } + else + { + GtkTreePath *c_path; + + elt->visible_siter = g_sequence_insert_sorted (level->visible_seq, elt, + filter_elt_cmp, NULL); + + c_path = gtk_tree_model_get_path (filter->priv->child_model, + &c_iter); + + gtk_tree_model_filter_emit_row_inserted_for_path (filter, + filter->priv->child_model, + c_path, + &c_iter); + + gtk_tree_path_free (c_path); + } + + /* We can immediately return, because this node was not visible + * before and the parent will check its children, including + * this node, in response to the emitted row-has-child-toggled + * signal. + */ + return; + } + + /* Not visible, so no need to continue. */ + return; + } + + if (!elt->children) + { + /* If an elt does not have children, these are not visible. + * Therefore, any signals emitted for these children will + * be ignored, so we do not have to emit them. + */ + return; + } + + level = elt->children; + i++; + + tmp_iter = c_iter; + gtk_tree_model_iter_nth_child (filter->priv->child_model, &c_iter, + &tmp_iter, indices[i]); + } +} + +static FilterElt * +gtk_tree_model_filter_insert_elt_in_level (GtkTreeModelFilter *filter, + GtkTreeIter *c_iter, + FilterLevel *level, + int offset, + int *index) +{ + FilterElt *elt; + GSequenceIter *siter; + + elt = filter_elt_new (); + + if (GTK_TREE_MODEL_FILTER_CACHE_CHILD_ITERS (filter)) + elt->iter = *c_iter; + + elt->offset = offset; + elt->zero_ref_count = 0; + elt->ref_count = 0; + elt->ext_ref_count = 0; + elt->children = NULL; + + /* Because we don't emit row_inserted, the node is invisible and thus + * not inserted in visible_seq + */ + elt->visible_siter = NULL; + + siter = g_sequence_insert_sorted (level->seq, elt, filter_elt_cmp, NULL); + *index = g_sequence_iter_get_position (siter); + + /* If the insert location is zero, we need to move our reference + * on the old first node to the new first node. + */ + if (*index == 0) + gtk_tree_model_filter_level_transfer_first_ref_with_index (filter, level, + 1, 0); + + return elt; +} + +static FilterElt * +gtk_tree_model_filter_fetch_child (GtkTreeModelFilter *filter, + FilterLevel *level, + int offset, + int *index) +{ + int len; + GtkTreePath *c_path = NULL; + GtkTreeIter c_iter; + GtkTreePath *c_parent_path = NULL; + GtkTreeIter c_parent_iter; + + /* check if child exists and is visible */ + if (level->parent_elt) + { + c_parent_path = + gtk_tree_model_filter_elt_get_path (level->parent_level, + level->parent_elt, + filter->priv->virtual_root); + if (!c_parent_path) + return NULL; + } + else + { + if (filter->priv->virtual_root) + c_parent_path = gtk_tree_path_copy (filter->priv->virtual_root); + else + c_parent_path = NULL; + } + + if (c_parent_path) + { + gtk_tree_model_get_iter (filter->priv->child_model, + &c_parent_iter, + c_parent_path); + len = gtk_tree_model_iter_n_children (filter->priv->child_model, + &c_parent_iter); + + c_path = gtk_tree_path_copy (c_parent_path); + gtk_tree_path_free (c_parent_path); + } + else + { + len = gtk_tree_model_iter_n_children (filter->priv->child_model, NULL); + c_path = gtk_tree_path_new (); + } + + gtk_tree_path_append_index (c_path, offset); + gtk_tree_model_get_iter (filter->priv->child_model, &c_iter, c_path); + gtk_tree_path_free (c_path); + + if (offset >= len || !gtk_tree_model_filter_visible (filter, &c_iter)) + return NULL; + + return gtk_tree_model_filter_insert_elt_in_level (filter, &c_iter, + level, offset, + index); +} + +/* Note that this function is never called from the row-deleted handler. + * This means that this function is only used for removing elements + * which are still present in the child model. As a result, we must + * take care to properly release the references the filter model has + * on the child model nodes. + */ +static void +gtk_tree_model_filter_remove_elt_from_level (GtkTreeModelFilter *filter, + FilterLevel *level, + FilterElt *elt) +{ + FilterElt *parent; + FilterLevel *parent_level; + int length, orig_level_ext_ref_count; + GtkTreeIter iter; + GtkTreePath *path = NULL; + + gboolean emit_child_toggled = FALSE; + + /* We need to know about the level's ext ref count before removal + * of this node. + */ + orig_level_ext_ref_count = level->ext_ref_count; + + iter.stamp = filter->priv->stamp; + iter.user_data = level; + iter.user_data2 = elt; + + parent = level->parent_elt; + parent_level = level->parent_level; + + if (!parent || orig_level_ext_ref_count > 0) + path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter); + else + /* If the level is not visible, the parent is potentially invisible + * too. Either way, as no signal will be emitted, there is no use + * for a path. + */ + path = NULL; + + length = g_sequence_get_length (level->seq); + + /* first register the node to be invisible */ + g_sequence_remove (elt->visible_siter); + elt->visible_siter = NULL; + + /* + * If level != root level and the number of visible nodes is 0 (ie. this + * is the last node to be removed from the level), emit + * row-has-child-toggled. + */ + + if (level != filter->priv->root + && g_sequence_get_length (level->visible_seq) == 0 + && parent + && parent->visible_siter) + emit_child_toggled = TRUE; + + /* Distinguish: + * - length > 1: in this case, the node is removed from the level + * and row-deleted is emitted. + * - length == 1: in this case, we need to decide whether to keep + * the level or to free it. + */ + if (length > 1) + { + GSequenceIter *siter; + + /* We emit row-deleted, and remove the node from the cache. + * If it has any children, these will be removed here as well. + */ + + /* FIXME: I am not 100% sure it is always save to fully free the + * level here. Perhaps the state of the parent level, etc. has to + * be checked to make the right decision, like is done below for + * the case length == 1. + */ + if (elt->children) + gtk_tree_model_filter_free_level (filter, elt->children, TRUE, TRUE, TRUE); + + /* If the first node is being removed, transfer, the reference */ + if (elt == g_sequence_get (g_sequence_get_begin_iter (level->seq))) + { + gtk_tree_model_filter_level_transfer_first_ref_with_index (filter, level, + 0, 1); + } + + while (elt->ext_ref_count > 0) + gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter), + &iter, TRUE, TRUE); + /* In this case, we do remove reference counts we've added ourselves, + * since the node will be removed from the data structures. + */ + while (elt->ref_count > 0) + gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter), + &iter, FALSE, TRUE); + + /* remove the node */ + lookup_elt_with_offset (level->seq, elt->offset, &siter); + g_sequence_remove (siter); + + gtk_tree_model_filter_increment_stamp (filter); + + /* Only if the node is in the root level (parent == NULL) or + * the level is visible, a row-deleted signal is necessary. + */ + if (!parent || orig_level_ext_ref_count > 0) + gtk_tree_model_row_deleted (GTK_TREE_MODEL (filter), path); + } + else + { + /* There is only one node left in this level */ +#ifdef MODEL_FILTER_DEBUG + g_assert (length == 1); +#endif + + /* The row is signalled as deleted to the client. We have to + * drop the remaining external reference count here, the client + * will not do it. + * + * We keep the reference counts we've obtained ourselves. + */ + while (elt->ext_ref_count > 0) + gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter), + &iter, TRUE, TRUE); + + /* This level is still required if: + * - it is the root level + * - its parent level is the root level + * - its parent level has an external ref count > 0 + */ + if (! (level == filter->priv->root || + level->parent_level == filter->priv->root || + level->parent_level->ext_ref_count > 0)) + { + /* Otherwise, the level can be removed */ + gtk_tree_model_filter_free_level (filter, level, TRUE, TRUE, TRUE); + } + else + { + /* Level is kept, but we turn our attention to a child level. + * + * If level is not the root level, it is a child level with + * an ext ref count that is now 0. That means that any child level + * of elt can be removed. + */ + if (level != filter->priv->root) + { +#ifdef MODEL_FILTER_DEBUG + g_assert (level->ext_ref_count == 0); +#endif + if (elt->children) + gtk_tree_model_filter_free_level (filter, elt->children, + TRUE, TRUE, TRUE); + } + else + { + /* In this case, we want to keep the level with the first + * node pulled in to monitor for signals. + */ + if (elt->children) + gtk_tree_model_filter_prune_level (filter, elt->children); + } + } + + if (!parent || orig_level_ext_ref_count > 0) + gtk_tree_model_row_deleted (GTK_TREE_MODEL (filter), path); + } + + gtk_tree_path_free (path); + + if (emit_child_toggled && parent->ext_ref_count > 0) + { + GtkTreeIter piter; + GtkTreePath *ppath; + + piter.stamp = filter->priv->stamp; + piter.user_data = parent_level; + piter.user_data2 = parent; + + ppath = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &piter); + + gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (filter), + ppath, &piter); + gtk_tree_path_free (ppath); + } +} + +/* This function is called after the given node has become visible. + * When the node has children, we should build the level and + * take a reference on the first child. + */ +static void +gtk_tree_model_filter_update_children (GtkTreeModelFilter *filter, + FilterLevel *level, + FilterElt *elt) +{ + GtkTreeIter c_iter; + GtkTreeIter iter; + + if (!elt->visible_siter) + return; + + iter.stamp = filter->priv->stamp; + iter.user_data = level; + iter.user_data2 = elt; + + gtk_tree_model_filter_convert_iter_to_child_iter (filter, &c_iter, &iter); + + if ((!level->parent_level || level->parent_level->ext_ref_count > 0) && + gtk_tree_model_iter_has_child (filter->priv->child_model, &c_iter)) + { + if (!elt->children) + gtk_tree_model_filter_build_level (filter, level, elt, FALSE); + + if (elt->ext_ref_count > 0 && elt->children && + g_sequence_get_length (elt->children->seq)) + { + GtkTreePath *path; + path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter); + gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (filter), + path, + &iter); + if (path) + gtk_tree_path_free (path); + } + } +} + +/* Path is relative to the child model (this is on search on elt offset) + * but with the virtual root already removed if necesssary. + */ +static gboolean +find_elt_with_offset (GtkTreeModelFilter *filter, + GtkTreePath *path, + FilterLevel **level_, + FilterElt **elt_) +{ + int i = 0; + FilterLevel *level; + FilterLevel *parent_level = NULL; + FilterElt *elt = NULL; + + level = FILTER_LEVEL (filter->priv->root); + + while (i < gtk_tree_path_get_depth (path)) + { + if (!level) + return FALSE; + + elt = lookup_elt_with_offset (level->seq, + gtk_tree_path_get_indices (path)[i], + NULL); + + if (!elt) + return FALSE; + + parent_level = level; + level = elt->children; + i++; + } + + if (level_) + *level_ = parent_level; + + if (elt_) + *elt_ = elt; + + return TRUE; +} + +/* TreeModel signals */ +static void +gtk_tree_model_filter_emit_row_inserted_for_path (GtkTreeModelFilter *filter, + GtkTreeModel *c_model, + GtkTreePath *c_path, + GtkTreeIter *c_iter) +{ + FilterLevel *level; + FilterElt *elt; + GtkTreePath *path; + GtkTreeIter iter, children; + gboolean signals_emitted = FALSE; + + if (!filter->priv->root) + { + /* The root level has not been exposed to the view yet, so we + * need to emit signals for any node that is being inserted. + */ + gtk_tree_model_filter_build_level (filter, NULL, NULL, TRUE); + + /* Check if the root level was built. Then child levels + * that matter have also been built (due to update_children, + * which triggers iter_n_children). + */ + if (filter->priv->root && + g_sequence_get_length (FILTER_LEVEL (filter->priv->root)->visible_seq) > 0) + signals_emitted = TRUE; + } + + gtk_tree_model_filter_increment_stamp (filter); + + /* We need to disallow to build new levels, because we are then pulling + * in a child in an invisible level. We only want to find path if it + * is in a visible level (and thus has a parent that is visible). + */ + path = gtk_real_tree_model_filter_convert_child_path_to_path (filter, + c_path, + FALSE, + TRUE); + + if (!path) + /* parent is probably being filtered out */ + return; + + gtk_tree_model_filter_get_iter_full (GTK_TREE_MODEL (filter), &iter, path); + + level = FILTER_LEVEL (iter.user_data); + elt = FILTER_ELT (iter.user_data2); + + /* Make sure elt is visible. elt can already be visible in case + * it was pulled in above, so avoid inserted it into visible_seq twice. + */ + if (!elt->visible_siter) + { + elt->visible_siter = g_sequence_insert_sorted (level->visible_seq, + elt, filter_elt_cmp, + NULL); + } + + /* Check whether the node and all of its parents are visible */ + if (gtk_tree_model_filter_elt_is_visible_in_target (level, elt)) + { + /* visibility changed -- reget path */ + gtk_tree_path_free (path); + path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter); + + if (!signals_emitted && + (!level->parent_level || level->ext_ref_count > 0)) + gtk_tree_model_row_inserted (GTK_TREE_MODEL (filter), path, &iter); + + if (level->parent_level && level->parent_elt->ext_ref_count > 0 && + g_sequence_get_length (level->visible_seq) == 1) + { + /* We know that this is the first visible node in this level, so + * we need to emit row-has-child-toggled on the parent. This + * does not apply to the root level. + */ + + gtk_tree_path_up (path); + gtk_tree_model_get_iter (GTK_TREE_MODEL (filter), &iter, path); + + gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (filter), + path, + &iter); + } + + if (!signals_emitted + && gtk_tree_model_iter_children (c_model, &children, c_iter)) + gtk_tree_model_filter_update_children (filter, level, elt); + } + + gtk_tree_path_free (path); +} + +static void +gtk_tree_model_filter_row_changed (GtkTreeModel *c_model, + GtkTreePath *c_path, + GtkTreeIter *c_iter, + gpointer data) +{ + GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (data); + GtkTreeIter iter; + GtkTreeIter children; + GtkTreeIter real_c_iter; + GtkTreePath *path = NULL; + GtkTreePath *real_path = NULL; + + FilterElt *elt; + FilterLevel *level; + + gboolean requested_state; + gboolean current_state; + gboolean free_c_path = FALSE; + + g_return_if_fail (c_path != NULL || c_iter != NULL); + + if (!c_path) + { + c_path = gtk_tree_model_get_path (c_model, c_iter); + free_c_path = TRUE; + } + + if (filter->priv->virtual_root) + real_path = gtk_tree_model_filter_remove_root (c_path, + filter->priv->virtual_root); + else + real_path = gtk_tree_path_copy (c_path); + + if (c_iter) + real_c_iter = *c_iter; + else + gtk_tree_model_get_iter (c_model, &real_c_iter, c_path); + + /* is this node above the virtual root? */ + if (filter->priv->virtual_root && + (gtk_tree_path_get_depth (filter->priv->virtual_root) + >= gtk_tree_path_get_depth (c_path))) + goto done; + + /* what's the requested state? */ + requested_state = gtk_tree_model_filter_visible (filter, &real_c_iter); + + /* now, let's see whether the item is there */ + path = gtk_real_tree_model_filter_convert_child_path_to_path (filter, + c_path, + FALSE, + FALSE); + + if (path) + { + gtk_tree_model_filter_get_iter_full (GTK_TREE_MODEL (filter), + &iter, path); + current_state = FILTER_ELT (iter.user_data2)->visible_siter != NULL; + } + else + current_state = FALSE; + + if (current_state == FALSE && requested_state == FALSE) + /* no changes required */ + goto done; + + if (current_state == TRUE && requested_state == FALSE) + { + gtk_tree_model_filter_remove_elt_from_level (filter, + FILTER_LEVEL (iter.user_data), + FILTER_ELT (iter.user_data2)); + + if (real_path) + gtk_tree_model_filter_check_ancestors (filter, real_path); + + goto done; + } + + if (current_state == TRUE && requested_state == TRUE) + { + level = FILTER_LEVEL (iter.user_data); + elt = FILTER_ELT (iter.user_data2); + + if (gtk_tree_model_filter_elt_is_visible_in_target (level, elt)) + { + /* propagate the signal; also get a path taking only visible + * nodes into account. + */ + gtk_tree_path_free (path); + path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter); + + if (level->ext_ref_count > 0) + gtk_tree_model_row_changed (GTK_TREE_MODEL (filter), path, &iter); + + /* and update the children */ + if (gtk_tree_model_iter_children (c_model, &children, &real_c_iter)) + gtk_tree_model_filter_update_children (filter, level, elt); + } + + if (real_path) + gtk_tree_model_filter_check_ancestors (filter, real_path); + + goto done; + } + + /* only current == FALSE and requested == TRUE is left, + * pull in the child + */ + g_return_if_fail (current_state == FALSE && requested_state == TRUE); + + if (real_path) + gtk_tree_model_filter_check_ancestors (filter, real_path); + + gtk_tree_model_filter_emit_row_inserted_for_path (filter, c_model, + c_path, c_iter); + +done: + if (path) + gtk_tree_path_free (path); + + if (real_path) + gtk_tree_path_free (real_path); + + if (free_c_path) + gtk_tree_path_free (c_path); +} + +static void +gtk_tree_model_filter_row_inserted (GtkTreeModel *c_model, + GtkTreePath *c_path, + GtkTreeIter *c_iter, + gpointer data) +{ + GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (data); + GtkTreePath *real_path = NULL; + + GtkTreeIter real_c_iter; + + FilterElt *elt = NULL; + FilterLevel *level = NULL; + FilterLevel *parent_level = NULL; + GSequenceIter *siter; + FilterElt dummy; + + int i = 0, offset; + + gboolean free_c_path = FALSE; + gboolean emit_row_inserted = FALSE; + + g_return_if_fail (c_path != NULL || c_iter != NULL); + + if (!c_path) + { + c_path = gtk_tree_model_get_path (c_model, c_iter); + free_c_path = TRUE; + } + + if (c_iter) + real_c_iter = *c_iter; + else + gtk_tree_model_get_iter (c_model, &real_c_iter, c_path); + + /* the row has already been inserted. so we need to fixup the + * virtual root here first + */ + if (filter->priv->virtual_root) + { + if (gtk_tree_path_get_depth (filter->priv->virtual_root) >= + gtk_tree_path_get_depth (c_path)) + { + int depth; + int *v_indices, *c_indices; + gboolean common_prefix = TRUE; + + depth = gtk_tree_path_get_depth (c_path) - 1; + v_indices = gtk_tree_path_get_indices (filter->priv->virtual_root); + c_indices = gtk_tree_path_get_indices (c_path); + + for (i = 0; i < depth; i++) + if (v_indices[i] != c_indices[i]) + { + common_prefix = FALSE; + break; + } + + if (common_prefix && v_indices[depth] >= c_indices[depth]) + (v_indices[depth])++; + } + } + + /* subtract virtual root if necessary */ + if (filter->priv->virtual_root) + { + real_path = gtk_tree_model_filter_remove_root (c_path, + filter->priv->virtual_root); + /* not our child */ + if (!real_path) + goto done; + } + else + real_path = gtk_tree_path_copy (c_path); + + if (!filter->priv->root) + { + /* The root level has not been exposed to the view yet, so we + * need to emit signals for any node that is being inserted. + */ + gtk_tree_model_filter_build_level (filter, NULL, NULL, TRUE); + + /* Check if the root level was built. Then child levels + * that matter have also been built (due to update_children, + * which triggers iter_n_children). + */ + if (filter->priv->root) + { + emit_row_inserted = FALSE; + goto done; + } + } + + if (gtk_tree_path_get_depth (real_path) - 1 >= 1) + { + gboolean found = FALSE; + GtkTreePath *parent = gtk_tree_path_copy (real_path); + gtk_tree_path_up (parent); + + found = find_elt_with_offset (filter, parent, &parent_level, &elt); + + gtk_tree_path_free (parent); + + if (!found) + /* Parent is not in the cache and probably being filtered out */ + goto done; + + level = elt->children; + } + else + level = FILTER_LEVEL (filter->priv->root); + + if (!level) + { + if (elt && elt->visible_siter) + { + /* The level in which the new node should be inserted does not + * exist, but the parent, elt, does. If elt is visible, emit + * row-has-child-toggled. + */ + GtkTreePath *tmppath; + GtkTreeIter tmpiter; + + tmpiter.stamp = filter->priv->stamp; + tmpiter.user_data = parent_level; + tmpiter.user_data2 = elt; + + tmppath = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), + &tmpiter); + + if (tmppath) + { + gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (filter), + tmppath, &tmpiter); + gtk_tree_path_free (tmppath); + } + } + goto done; + } + + /* let's try to insert the value */ + offset = gtk_tree_path_get_indices (real_path)[gtk_tree_path_get_depth (real_path) - 1]; + + /* update the offsets, yes if we didn't insert the node above, there will + * be a gap here. This will be filled with the node (via fetch_child) when + * it becomes visible + */ + dummy.offset = offset; + siter = g_sequence_search (level->seq, &dummy, filter_elt_cmp, NULL); + siter = g_sequence_iter_prev (siter); + g_sequence_foreach_range (siter, g_sequence_get_end_iter (level->seq), + increase_offset_iter, GINT_TO_POINTER (offset)); + + /* only insert when visible */ + if (gtk_tree_model_filter_visible (filter, &real_c_iter)) + { + FilterElt *felt; + + felt = gtk_tree_model_filter_insert_elt_in_level (filter, + &real_c_iter, + level, offset, + &i); + + /* insert_elt_in_level defaults to FALSE */ + felt->visible_siter = g_sequence_insert_sorted (level->visible_seq, + felt, + filter_elt_cmp, NULL); + emit_row_inserted = TRUE; + } + +done: + if (real_path) + gtk_tree_model_filter_check_ancestors (filter, real_path); + + if (emit_row_inserted) + gtk_tree_model_filter_emit_row_inserted_for_path (filter, c_model, + c_path, c_iter); + + if (real_path) + gtk_tree_path_free (real_path); + + if (free_c_path) + gtk_tree_path_free (c_path); +} + +static void +gtk_tree_model_filter_row_has_child_toggled (GtkTreeModel *c_model, + GtkTreePath *c_path, + GtkTreeIter *c_iter, + gpointer data) +{ + GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (data); + GtkTreePath *path; + GtkTreeIter iter; + FilterLevel *level; + FilterElt *elt; + gboolean requested_state; + + g_return_if_fail (c_path != NULL && c_iter != NULL); + + /* If we get row-has-child-toggled on the virtual root, and there is + * no root level; try to build it now. + */ + if (filter->priv->virtual_root && !filter->priv->root + && !gtk_tree_path_compare (c_path, filter->priv->virtual_root)) + { + gtk_tree_model_filter_build_level (filter, NULL, NULL, TRUE); + return; + } + + /* For all other levels, there is a chance that the visibility state + * of the parent has changed now. + */ + + path = gtk_real_tree_model_filter_convert_child_path_to_path (filter, + c_path, + FALSE, + TRUE); + if (!path) + return; + + gtk_tree_model_filter_get_iter_full (GTK_TREE_MODEL (data), &iter, path); + + level = FILTER_LEVEL (iter.user_data); + elt = FILTER_ELT (iter.user_data2); + + gtk_tree_path_free (path); + + requested_state = gtk_tree_model_filter_visible (filter, c_iter); + + if (!elt->visible_siter && !requested_state) + { + /* The parent node currently is not visible and will not become + * visible, so we will not pass on the row-has-child-toggled event. + */ + return; + } + else if (elt->visible_siter && !requested_state) + { + /* The node is no longer visible, so it has to be removed. + * _remove_elt_from_level() takes care of emitting row-has-child-toggled + * when required. + */ + gtk_tree_model_filter_remove_elt_from_level (filter, level, elt); + + return; + } + else if (!elt->visible_siter && requested_state) + { + elt->visible_siter = g_sequence_insert_sorted (level->visible_seq, + elt, filter_elt_cmp, + NULL); + + /* Only insert if the parent is visible in the target */ + if (gtk_tree_model_filter_elt_is_visible_in_target (level, elt)) + { + path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter); + gtk_tree_model_row_inserted (GTK_TREE_MODEL (filter), path, &iter); + gtk_tree_path_free (path); + + /* We do not update children now, because that will happen + * below. + */ + } + } + /* For the remaining possibility, elt->visible && requested_state + * no action is required. + */ + + /* If this node is referenced and has children, build the level so we + * can monitor it for changes. + */ + if (elt->ref_count > 1 && !elt->children && + gtk_tree_model_iter_has_child (c_model, c_iter)) + gtk_tree_model_filter_build_level (filter, level, elt, FALSE); + + /* get a path taking only visible nodes into account */ + path = gtk_tree_model_get_path (GTK_TREE_MODEL (data), &iter); + gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (data), path, &iter); + gtk_tree_path_free (path); +} + +static void +gtk_tree_model_filter_virtual_root_deleted (GtkTreeModelFilter *filter, + GtkTreePath *c_path) +{ + int i, nodes; + GtkTreePath *path; + FilterLevel *level = FILTER_LEVEL (filter->priv->root); + + /* The virtual root (or one of its ancestors) has been deleted. This + * means that all content for our model is now gone. We deal with + * this by removing everything in the filter model: we just iterate + * over the root level and emit a row-deleted for each FilterElt. + * (FIXME: Should we emit row-deleted for child nodes as well? This + * has never been fully clear in TreeModel). + */ + + /* We unref the path of the virtual root, up to and not including the + * deleted node which can no longer be unreffed. + */ + gtk_tree_model_filter_unref_path (filter, filter->priv->virtual_root, + gtk_tree_path_get_depth (c_path) - 1); + filter->priv->virtual_root_deleted = TRUE; + + if (!level) + return; + + nodes = g_sequence_get_length (level->visible_seq); + + /* We should not propagate the unref here. An unref for any of these + * nodes will fail, since the respective nodes in the child model are + * no longer there. + */ + gtk_tree_model_filter_free_level (filter, filter->priv->root, FALSE, TRUE, FALSE); + + gtk_tree_model_filter_increment_stamp (filter); + + path = gtk_tree_path_new (); + gtk_tree_path_append_index (path, 0); + + for (i = 0; i < nodes; i++) + gtk_tree_model_row_deleted (GTK_TREE_MODEL (filter), path); + + gtk_tree_path_free (path); +} + +static void +gtk_tree_model_filter_adjust_virtual_root (GtkTreeModelFilter *filter, + GtkTreePath *c_path) +{ + int i; + int level; + int *v_indices, *c_indices; + gboolean common_prefix = TRUE; + + level = gtk_tree_path_get_depth (c_path) - 1; + v_indices = gtk_tree_path_get_indices (filter->priv->virtual_root); + c_indices = gtk_tree_path_get_indices (c_path); + + for (i = 0; i < level; i++) + if (v_indices[i] != c_indices[i]) + { + common_prefix = FALSE; + break; + } + + if (common_prefix && v_indices[level] > c_indices[level]) + (v_indices[level])--; +} + +static void +gtk_tree_model_filter_row_deleted_invisible_node (GtkTreeModelFilter *filter, + GtkTreePath *c_path) +{ + int offset; + GtkTreePath *real_path; + FilterLevel *level; + FilterElt *elt; + FilterElt dummy; + GSequenceIter *siter; + + /* The node deleted in the child model is not visible in the + * filter model. We will not emit a signal, just fixup the offsets + * of the other nodes. + */ + + if (!filter->priv->root) + return; + + level = FILTER_LEVEL (filter->priv->root); + + /* subtract vroot if necessary */ + if (filter->priv->virtual_root) + { + real_path = gtk_tree_model_filter_remove_root (c_path, + filter->priv->virtual_root); + /* we don't handle this */ + if (!real_path) + return; + } + else + real_path = gtk_tree_path_copy (c_path); + + if (gtk_tree_path_get_depth (real_path) - 1 >= 1) + { + gboolean found = FALSE; + GtkTreePath *parent = gtk_tree_path_copy (real_path); + gtk_tree_path_up (parent); + + found = find_elt_with_offset (filter, parent, &level, &elt); + + gtk_tree_path_free (parent); + + if (!found) + { + /* parent is filtered out, so no level */ + gtk_tree_path_free (real_path); + return; + } + + level = elt->children; + } + + offset = gtk_tree_path_get_indices (real_path)[gtk_tree_path_get_depth (real_path) - 1]; + gtk_tree_path_free (real_path); + + if (!level) + return; + + /* decrease offset of all nodes following the deleted node */ + dummy.offset = offset; + siter = g_sequence_search (level->seq, &dummy, filter_elt_cmp, NULL); + g_sequence_foreach_range (siter, g_sequence_get_end_iter (level->seq), + decrease_offset_iter, GINT_TO_POINTER (offset)); +} + +static void +gtk_tree_model_filter_row_deleted (GtkTreeModel *c_model, + GtkTreePath *c_path, + gpointer data) +{ + GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (data); + GtkTreePath *path; + GtkTreeIter iter; + FilterElt *elt, *parent_elt = NULL; + FilterLevel *level, *parent_level = NULL; + GSequenceIter *siter; + gboolean emit_child_toggled = FALSE; + gboolean emit_row_deleted = FALSE; + int offset; + int orig_level_ext_ref_count; + + g_return_if_fail (c_path != NULL); + + /* special case the deletion of an ancestor of the virtual root */ + if (filter->priv->virtual_root && + (gtk_tree_path_is_ancestor (c_path, filter->priv->virtual_root) || + !gtk_tree_path_compare (c_path, filter->priv->virtual_root))) + { + gtk_tree_model_filter_virtual_root_deleted (filter, c_path); + return; + } + + /* adjust the virtual root for the deleted row */ + if (filter->priv->virtual_root && + gtk_tree_path_get_depth (filter->priv->virtual_root) >= + gtk_tree_path_get_depth (c_path)) + gtk_tree_model_filter_adjust_virtual_root (filter, c_path); + + path = gtk_real_tree_model_filter_convert_child_path_to_path (filter, + c_path, + FALSE, + FALSE); + + if (!path) + { + gtk_tree_model_filter_row_deleted_invisible_node (filter, c_path); + return; + } + + /* a node was deleted, which was in our cache */ + gtk_tree_model_filter_get_iter_full (GTK_TREE_MODEL (data), &iter, path); + + level = FILTER_LEVEL (iter.user_data); + elt = FILTER_ELT (iter.user_data2); + offset = elt->offset; + orig_level_ext_ref_count = level->ext_ref_count; + + if (elt->visible_siter) + { + /* get a path taking only visible nodes into account */ + gtk_tree_path_free (path); + path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter); + + if (g_sequence_get_length (level->visible_seq) == 1) + { + emit_child_toggled = TRUE; + parent_level = level->parent_level; + parent_elt = level->parent_elt; + } + + emit_row_deleted = TRUE; + } + + /* Release the references on this node, without propagation because + * the node does not exist anymore in the child model. The filter + * model's references on the node in case of level->parent or use + * of a virtual root are automatically destroyed by the child model. + */ + while (elt->ext_ref_count > 0) + gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (data), &iter, + TRUE, FALSE); + + if (elt->children) + /* If this last node has children, then the recursion in free_level + * will release this reference. + */ + while (elt->ref_count > 1) + gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (data), &iter, + FALSE, FALSE); + else + while (elt->ref_count > 0) + gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (data), &iter, + FALSE, FALSE); + + + if (g_sequence_get_length (level->seq) == 1) + { + /* kill level */ + gtk_tree_model_filter_free_level (filter, level, FALSE, TRUE, FALSE); + } + else + { + GSequenceIter *tmp; + gboolean is_first; + + lookup_elt_with_offset (level->seq, elt->offset, &siter); + is_first = g_sequence_get_begin_iter (level->seq) == siter; + + if (elt->children) + gtk_tree_model_filter_free_level (filter, elt->children, + FALSE, FALSE, FALSE); + + /* remove the row */ + if (elt->visible_siter) + g_sequence_remove (elt->visible_siter); + tmp = g_sequence_iter_next (siter); + g_sequence_remove (siter); + g_sequence_foreach_range (tmp, g_sequence_get_end_iter (level->seq), + decrease_offset_iter, GINT_TO_POINTER (offset)); + + /* Take a reference on the new first node. The first node previously + * keeping this reference has been removed above. + */ + if (is_first) + { + GtkTreeIter f_iter; + + f_iter.stamp = filter->priv->stamp; + f_iter.user_data = level; + f_iter.user_data2 = g_sequence_get (g_sequence_get_begin_iter (level->seq)); + + gtk_tree_model_filter_real_ref_node (GTK_TREE_MODEL (filter), + &f_iter, FALSE); + } + } + + if (emit_row_deleted) + { + /* emit row_deleted */ + gtk_tree_model_filter_increment_stamp (filter); + + if (!parent_elt || orig_level_ext_ref_count > 0) + gtk_tree_model_row_deleted (GTK_TREE_MODEL (data), path); + } + + if (emit_child_toggled && parent_level) + { + GtkTreeIter iter2; + GtkTreePath *path2; + + iter2.stamp = filter->priv->stamp; + iter2.user_data = parent_level; + iter2.user_data2 = parent_elt; + + /* We set in_row_deleted to TRUE to avoid a level build triggered + * by row-has-child-toggled (parent model could call iter_has_child + * for example). + */ + filter->priv->in_row_deleted = TRUE; + path2 = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter2); + gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (filter), + path2, &iter2); + gtk_tree_path_free (path2); + filter->priv->in_row_deleted = FALSE; + } + + if (filter->priv->virtual_root) + { + GtkTreePath *real_path; + + real_path = gtk_tree_model_filter_remove_root (c_path, + filter->priv->virtual_root); + if (real_path) + { + gtk_tree_model_filter_check_ancestors (filter, real_path); + gtk_tree_path_free (real_path); + } + } + else + gtk_tree_model_filter_check_ancestors (filter, c_path); + + gtk_tree_path_free (path); +} + +static void +gtk_tree_model_filter_rows_reordered (GtkTreeModel *c_model, + GtkTreePath *c_path, + GtkTreeIter *c_iter, + int *new_order, + gpointer data) +{ + FilterElt *elt; + FilterLevel *level; + GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (data); + + GtkTreePath *path; + GtkTreeIter iter; + + GSequence *tmp_seq; + GSequenceIter *tmp_end_iter; + GSequenceIter *old_first_siter = NULL; + int *tmp_array; + int i, elt_count; + int length; + + g_return_if_fail (new_order != NULL); + + if (c_path == NULL || gtk_tree_path_get_depth (c_path) == 0) + { + length = gtk_tree_model_iter_n_children (c_model, NULL); + + if (filter->priv->virtual_root) + { + int new_pos = -1; + + /* reorder root level of path */ + for (i = 0; i < length; i++) + if (new_order[i] == gtk_tree_path_get_indices (filter->priv->virtual_root)[0]) + new_pos = i; + + if (new_pos < 0) + return; + + gtk_tree_path_get_indices (filter->priv->virtual_root)[0] = new_pos; + return; + } + + path = gtk_tree_path_new (); + level = FILTER_LEVEL (filter->priv->root); + } + else + { + GtkTreeIter child_iter; + + /* virtual root anchor reordering */ + if (filter->priv->virtual_root && + gtk_tree_path_is_ancestor (c_path, filter->priv->virtual_root)) + { + int new_pos = -1; + int len; + int depth; + GtkTreeIter real_c_iter; + + depth = gtk_tree_path_get_depth (c_path); + + if (c_iter) + real_c_iter = *c_iter; + else + gtk_tree_model_get_iter (c_model, &real_c_iter, c_path); + + len = gtk_tree_model_iter_n_children (c_model, &real_c_iter); + + for (i = 0; i < len; i++) + if (new_order[i] == gtk_tree_path_get_indices (filter->priv->virtual_root)[depth]) + new_pos = i; + + if (new_pos < 0) + return; + + gtk_tree_path_get_indices (filter->priv->virtual_root)[depth] = new_pos; + return; + } + + path = gtk_real_tree_model_filter_convert_child_path_to_path (filter, + c_path, + FALSE, + FALSE); + + if (!path && filter->priv->virtual_root && + gtk_tree_path_compare (c_path, filter->priv->virtual_root)) + return; + + if (!path && !filter->priv->virtual_root) + return; + + if (!path) + { + /* root level mode */ + if (!c_iter) + gtk_tree_model_get_iter (c_model, c_iter, c_path); + length = gtk_tree_model_iter_n_children (c_model, c_iter); + path = gtk_tree_path_new (); + level = FILTER_LEVEL (filter->priv->root); + } + else + { + gtk_tree_model_filter_get_iter_full (GTK_TREE_MODEL (data), + &iter, path); + + elt = FILTER_ELT (iter.user_data2); + + if (!elt->children) + { + gtk_tree_path_free (path); + return; + } + + level = elt->children; + + gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (filter), &child_iter, &iter); + length = gtk_tree_model_iter_n_children (c_model, &child_iter); + } + } + + if (!level || g_sequence_get_length (level->seq) < 1) + { + gtk_tree_path_free (path); + return; + } + + /* NOTE: we do not bail out here if level->seq->len < 2 like + * GtkTreeModelSort does. This because we do some special tricky + * reordering. + */ + + tmp_seq = g_sequence_new (filter_elt_free); + tmp_end_iter = g_sequence_get_end_iter (tmp_seq); + tmp_array = g_new (int, g_sequence_get_length (level->visible_seq)); + elt_count = 0; + + old_first_siter = g_sequence_get_iter_at_pos (level->seq, 0); + + for (i = 0; i < length; i++) + { + GSequenceIter *siter; + + elt = lookup_elt_with_offset (level->seq, new_order[i], &siter); + if (elt == NULL) + continue; + + /* Only for visible items an entry should be present in the order array + * to be emitted. + */ + if (elt->visible_siter) + tmp_array[elt_count++] = g_sequence_iter_get_position (elt->visible_siter); + + /* Steal elt from level->seq and append it to tmp_seq */ + g_sequence_move (siter, tmp_end_iter); + elt->offset = i; + } + + g_warn_if_fail (g_sequence_get_length (level->seq) == 0); + g_sequence_free (level->seq); + level->seq = tmp_seq; + g_sequence_sort (level->visible_seq, filter_elt_cmp, NULL); + + /* Transfer the reference from the old item at position 0 to the + * new item at position 0, unless the old item at position 0 is also + * at position 0 in the new sequence. + */ + if (g_sequence_iter_get_position (old_first_siter) != 0) + gtk_tree_model_filter_level_transfer_first_ref (filter, + level, + old_first_siter, + g_sequence_get_iter_at_pos (level->seq, 0)); + + /* emit rows_reordered */ + if (g_sequence_get_length (level->visible_seq) > 0) + { + if (!gtk_tree_path_get_indices (path)) + gtk_tree_model_rows_reordered (GTK_TREE_MODEL (data), path, NULL, + tmp_array); + else + { + /* get a path taking only visible nodes into account */ + gtk_tree_path_free (path); + path = gtk_tree_model_get_path (GTK_TREE_MODEL (data), &iter); + + gtk_tree_model_rows_reordered (GTK_TREE_MODEL (data), path, &iter, + tmp_array); + } + } + + /* done */ + g_free (tmp_array); + gtk_tree_path_free (path); +} + +/* TreeModelIface implementation */ +static GtkTreeModelFlags +gtk_tree_model_filter_get_flags (GtkTreeModel *model) +{ + GtkTreeModelFlags flags; + + g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), 0); + g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->child_model != NULL, 0); + + flags = gtk_tree_model_get_flags (GTK_TREE_MODEL_FILTER (model)->priv->child_model); + + if ((flags & GTK_TREE_MODEL_LIST_ONLY) == GTK_TREE_MODEL_LIST_ONLY) + return GTK_TREE_MODEL_LIST_ONLY; + + return 0; +} + +static int +gtk_tree_model_filter_get_n_columns (GtkTreeModel *model) +{ + GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model; + + g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), 0); + g_return_val_if_fail (filter->priv->child_model != NULL, 0); + + if (filter->priv->child_model == NULL) + return 0; + + /* so we can't set the modify func after this ... */ + filter->priv->modify_func_set = TRUE; + + if (filter->priv->modify_n_columns > 0) + return filter->priv->modify_n_columns; + + return gtk_tree_model_get_n_columns (filter->priv->child_model); +} + +static GType +gtk_tree_model_filter_get_column_type (GtkTreeModel *model, + int index) +{ + GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model; + + g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), G_TYPE_INVALID); + g_return_val_if_fail (filter->priv->child_model != NULL, G_TYPE_INVALID); + + /* so we can't set the modify func after this ... */ + filter->priv->modify_func_set = TRUE; + + if (filter->priv->modify_types) + { + g_return_val_if_fail (index < filter->priv->modify_n_columns, G_TYPE_INVALID); + + return filter->priv->modify_types[index]; + } + + return gtk_tree_model_get_column_type (filter->priv->child_model, index); +} + +/* A special case of _get_iter; this function can also get iters which + * are not visible. These iters should ONLY be passed internally, never + * pass those along with a signal emission. + */ +static gboolean +gtk_tree_model_filter_get_iter_full (GtkTreeModel *model, + GtkTreeIter *iter, + GtkTreePath *path) +{ + GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model; + int *indices; + FilterLevel *level; + FilterElt *elt; + int depth, i; + GSequenceIter *siter; + + g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), FALSE); + g_return_val_if_fail (filter->priv->child_model != NULL, FALSE); + + indices = gtk_tree_path_get_indices (path); + + if (filter->priv->root == NULL) + gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE); + level = FILTER_LEVEL (filter->priv->root); + + depth = gtk_tree_path_get_depth (path); + if (!depth) + { + iter->stamp = 0; + return FALSE; + } + + for (i = 0; i < depth - 1; i++) + { + if (!level || indices[i] >= g_sequence_get_length (level->seq)) + { + iter->stamp = 0; + return FALSE; + } + + siter = g_sequence_get_iter_at_pos (level->seq, indices[i]); + if (g_sequence_iter_is_end (siter)) + { + iter->stamp = 0; + return FALSE; + } + + elt = GET_ELT (siter); + g_assert (elt); + + if (!elt->children) + gtk_tree_model_filter_build_level (filter, level, elt, FALSE); + level = elt->children; + } + + if (!level || indices[i] >= g_sequence_get_length (level->seq)) + { + iter->stamp = 0; + return FALSE; + } + + iter->stamp = filter->priv->stamp; + iter->user_data = level; + + siter = g_sequence_get_iter_at_pos (level->seq, indices[depth - 1]); + if (g_sequence_iter_is_end (siter)) + { + iter->stamp = 0; + return FALSE; + } + iter->user_data2 = GET_ELT (siter); + + return TRUE; +} + +static gboolean +gtk_tree_model_filter_get_iter (GtkTreeModel *model, + GtkTreeIter *iter, + GtkTreePath *path) +{ + GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model; + int *indices; + FilterLevel *level; + FilterElt *elt; + GSequenceIter *siter; + int depth, i; + + g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), FALSE); + g_return_val_if_fail (filter->priv->child_model != NULL, FALSE); + + indices = gtk_tree_path_get_indices (path); + + if (filter->priv->root == NULL) + gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE); + level = FILTER_LEVEL (filter->priv->root); + + depth = gtk_tree_path_get_depth (path); + if (!depth) + { + iter->stamp = 0; + return FALSE; + } + + for (i = 0; i < depth - 1; i++) + { + if (!level || indices[i] >= g_sequence_get_length (level->visible_seq)) + { + iter->stamp = 0; + return FALSE; + } + + siter = g_sequence_get_iter_at_pos (level->visible_seq, indices[i]); + if (g_sequence_iter_is_end (siter)) + { + iter->stamp = 0; + return FALSE; + } + + elt = GET_ELT (siter); + g_assert (elt); + if (!elt->children) + gtk_tree_model_filter_build_level (filter, level, elt, FALSE); + level = elt->children; + } + + if (!level || indices[i] >= g_sequence_get_length (level->visible_seq)) + { + iter->stamp = 0; + return FALSE; + } + + iter->stamp = filter->priv->stamp; + iter->user_data = level; + + siter = g_sequence_get_iter_at_pos (level->visible_seq, indices[depth - 1]); + if (g_sequence_iter_is_end (siter)) + { + iter->stamp = 0; + return FALSE; + } + iter->user_data2 = GET_ELT (siter); + + return TRUE; +} + +static GtkTreePath * +gtk_tree_model_filter_get_path (GtkTreeModel *model, + GtkTreeIter *iter) +{ + GtkTreePath *retval; + FilterLevel *level; + FilterElt *elt; + + g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), NULL); + g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->child_model != NULL, NULL); + g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->stamp == iter->stamp, NULL); + + level = iter->user_data; + elt = iter->user_data2; + + if (!elt->visible_siter) + return NULL; + + retval = gtk_tree_path_new (); + + while (level) + { + int index; + + index = g_sequence_iter_get_position (elt->visible_siter); + gtk_tree_path_prepend_index (retval, index); + + elt = level->parent_elt; + level = level->parent_level; + } + + return retval; +} + +static void +gtk_tree_model_filter_real_modify (GtkTreeModelFilter *self, + GtkTreeModel *child_model, + GtkTreeIter *iter, + GValue *value, + int column) +{ + if (self->priv->modify_func) + { + g_return_if_fail (column < self->priv->modify_n_columns); + + g_value_init (value, self->priv->modify_types[column]); + self->priv->modify_func (GTK_TREE_MODEL (self), + iter, value, column, + self->priv->modify_data); + } + else + { + GtkTreeIter child_iter; + + gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (self), + &child_iter, iter); + gtk_tree_model_get_value (child_model, &child_iter, column, value); + } +} + +static void +gtk_tree_model_filter_get_value (GtkTreeModel *model, + GtkTreeIter *iter, + int column, + GValue *value) +{ + GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (model); + + g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (model)); + g_return_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->child_model != NULL); + g_return_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->stamp == iter->stamp); + + GTK_TREE_MODEL_FILTER_GET_CLASS (model)->modify (filter, + filter->priv->child_model, iter, value, column); +} + +static gboolean +gtk_tree_model_filter_iter_next (GtkTreeModel *model, + GtkTreeIter *iter) +{ + FilterElt *elt; + GSequenceIter *siter; + + g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), FALSE); + g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->child_model != NULL, FALSE); + g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->stamp == iter->stamp, FALSE); + + elt = iter->user_data2; + + siter = g_sequence_iter_next (elt->visible_siter); + if (g_sequence_iter_is_end (siter)) + { + iter->stamp = 0; + return FALSE; + } + + iter->user_data2 = GET_ELT (siter); + + return TRUE; +} + +static gboolean +gtk_tree_model_filter_iter_previous (GtkTreeModel *model, + GtkTreeIter *iter) +{ + FilterElt *elt; + GSequenceIter *siter; + + g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), FALSE); + g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->child_model != NULL, FALSE); + g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->stamp == iter->stamp, FALSE); + + elt = iter->user_data2; + + if (g_sequence_iter_is_begin (elt->visible_siter)) + { + iter->stamp = 0; + return FALSE; + } + siter = g_sequence_iter_prev (elt->visible_siter); + + iter->user_data2 = GET_ELT (siter); + + return TRUE; +} + +static gboolean +gtk_tree_model_filter_iter_children (GtkTreeModel *model, + GtkTreeIter *iter, + GtkTreeIter *parent) +{ + GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model; + FilterLevel *level; + GSequenceIter *siter; + + iter->stamp = 0; + g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), FALSE); + g_return_val_if_fail (filter->priv->child_model != NULL, FALSE); + if (parent) + g_return_val_if_fail (filter->priv->stamp == parent->stamp, FALSE); + + if (!parent) + { + if (!filter->priv->root) + gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE); + if (!filter->priv->root) + return FALSE; + + level = filter->priv->root; + siter = g_sequence_get_begin_iter (level->visible_seq); + if (g_sequence_iter_is_end (siter)) + { + iter->stamp = 0; + return FALSE; + } + + iter->stamp = filter->priv->stamp; + iter->user_data = level; + iter->user_data2 = GET_ELT (siter); + + return TRUE; + } + else + { + if (FILTER_ELT (parent->user_data2)->children == NULL) + gtk_tree_model_filter_build_level (filter, + FILTER_LEVEL (parent->user_data), + FILTER_ELT (parent->user_data2), + FALSE); + if (FILTER_ELT (parent->user_data2)->children == NULL) + return FALSE; + + level = FILTER_ELT (parent->user_data2)->children; + siter = g_sequence_get_begin_iter (level->visible_seq); + if (g_sequence_iter_is_end (siter)) + { + iter->stamp = 0; + return FALSE; + } + + iter->stamp = filter->priv->stamp; + iter->user_data = level; + iter->user_data2 = GET_ELT (siter); + + return TRUE; + } +} + +static gboolean +gtk_tree_model_filter_iter_has_child (GtkTreeModel *model, + GtkTreeIter *iter) +{ + GtkTreeIter child_iter; + GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model; + FilterElt *elt; + + g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), FALSE); + g_return_val_if_fail (filter->priv->child_model != NULL, FALSE); + g_return_val_if_fail (filter->priv->stamp == iter->stamp, FALSE); + + filter = GTK_TREE_MODEL_FILTER (model); + + gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_iter, iter); + elt = FILTER_ELT (iter->user_data2); + + if (!elt->visible_siter) + return FALSE; + + /* we need to build the level to check if not all children are filtered + * out + */ + if (!elt->children + && gtk_tree_model_iter_has_child (filter->priv->child_model, &child_iter)) + gtk_tree_model_filter_build_level (filter, FILTER_LEVEL (iter->user_data), + elt, FALSE); + + if (elt->children && g_sequence_get_length (elt->children->visible_seq) > 0) + return TRUE; + + return FALSE; +} + +static int +gtk_tree_model_filter_iter_n_children (GtkTreeModel *model, + GtkTreeIter *iter) +{ + GtkTreeIter child_iter; + GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model; + FilterElt *elt; + + g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), 0); + g_return_val_if_fail (filter->priv->child_model != NULL, 0); + if (iter) + g_return_val_if_fail (filter->priv->stamp == iter->stamp, 0); + + if (!iter) + { + if (!filter->priv->root) + gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE); + + if (filter->priv->root) + return g_sequence_get_length (FILTER_LEVEL (filter->priv->root)->visible_seq); + + return 0; + } + + elt = FILTER_ELT (iter->user_data2); + + if (!elt->visible_siter) + return 0; + + gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_iter, iter); + + if (!elt->children && + gtk_tree_model_iter_has_child (filter->priv->child_model, &child_iter)) + gtk_tree_model_filter_build_level (filter, + FILTER_LEVEL (iter->user_data), + elt, FALSE); + + if (elt->children) + return g_sequence_get_length (elt->children->visible_seq); + + return 0; +} + +static gboolean +gtk_tree_model_filter_iter_nth_child (GtkTreeModel *model, + GtkTreeIter *iter, + GtkTreeIter *parent, + int n) +{ + FilterLevel *level; + GtkTreeIter children; + GSequenceIter *siter; + + g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), FALSE); + if (parent) + g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->stamp == parent->stamp, FALSE); + + /* use this instead of has_child to force us to build the level, if needed */ + if (gtk_tree_model_filter_iter_children (model, &children, parent) == FALSE) + { + iter->stamp = 0; + return FALSE; + } + + level = children.user_data; + siter = g_sequence_get_iter_at_pos (level->visible_seq, n); + if (g_sequence_iter_is_end (siter)) + return FALSE; + + iter->stamp = GTK_TREE_MODEL_FILTER (model)->priv->stamp; + iter->user_data = level; + iter->user_data2 = GET_ELT (siter); + + return TRUE; +} + +static gboolean +gtk_tree_model_filter_iter_parent (GtkTreeModel *model, + GtkTreeIter *iter, + GtkTreeIter *child) +{ + FilterLevel *level; + + iter->stamp = 0; + g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), FALSE); + g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->child_model != NULL, FALSE); + g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->stamp == child->stamp, FALSE); + + level = child->user_data; + + if (level->parent_level) + { + iter->stamp = GTK_TREE_MODEL_FILTER (model)->priv->stamp; + iter->user_data = level->parent_level; + iter->user_data2 = level->parent_elt; + + return TRUE; + } + + return FALSE; +} + +static void +gtk_tree_model_filter_ref_node (GtkTreeModel *model, + GtkTreeIter *iter) +{ + gtk_tree_model_filter_real_ref_node (model, iter, TRUE); +} + +static void +gtk_tree_model_filter_real_ref_node (GtkTreeModel *model, + GtkTreeIter *iter, + gboolean external) +{ + GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model; + GtkTreeIter child_iter; + FilterLevel *level; + FilterElt *elt; + + g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (model)); + g_return_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->child_model != NULL); + g_return_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->stamp == iter->stamp); + + gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_iter, iter); + + gtk_tree_model_ref_node (filter->priv->child_model, &child_iter); + + level = iter->user_data; + elt = iter->user_data2; + + elt->ref_count++; + level->ref_count++; + + if (external) + { + elt->ext_ref_count++; + level->ext_ref_count++; + + if (level->ext_ref_count == 1) + { + FilterLevel *parent_level = level->parent_level; + FilterElt *parent_elt = level->parent_elt; + + /* we were at zero -- time to decrease the zero_ref_count val */ + while (parent_level) + { + parent_elt->zero_ref_count--; + + parent_elt = parent_level->parent_elt; + parent_level = parent_level->parent_level; + } + + if (filter->priv->root != level) + filter->priv->zero_ref_count--; + +#ifdef MODEL_FILTER_DEBUG + g_assert (filter->priv->zero_ref_count >= 0); + if (filter->priv->zero_ref_count > 0) + g_assert (filter->priv->root != NULL); +#endif + } + } + +#ifdef MODEL_FILTER_DEBUG + g_assert (elt->ref_count >= elt->ext_ref_count); + g_assert (elt->ref_count >= 0); + g_assert (elt->ext_ref_count >= 0); +#endif +} + +static void +gtk_tree_model_filter_unref_node (GtkTreeModel *model, + GtkTreeIter *iter) +{ + gtk_tree_model_filter_real_unref_node (model, iter, TRUE, TRUE); +} + +static void +gtk_tree_model_filter_real_unref_node (GtkTreeModel *model, + GtkTreeIter *iter, + gboolean external, + gboolean propagate_unref) +{ + GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model; + FilterLevel *level; + FilterElt *elt; + + g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (model)); + g_return_if_fail (filter->priv->child_model != NULL); + g_return_if_fail (filter->priv->stamp == iter->stamp); + + if (propagate_unref) + { + GtkTreeIter child_iter; + gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_iter, iter); + gtk_tree_model_unref_node (filter->priv->child_model, &child_iter); + } + + level = iter->user_data; + elt = iter->user_data2; + + g_return_if_fail (elt->ref_count > 0); +#ifdef MODEL_FILTER_DEBUG + g_assert (elt->ref_count >= elt->ext_ref_count); + g_assert (elt->ref_count >= 0); + g_assert (elt->ext_ref_count >= 0); +#endif + + elt->ref_count--; + level->ref_count--; + + if (external) + { + elt->ext_ref_count--; + level->ext_ref_count--; + + if (level->ext_ref_count == 0) + { + FilterLevel *parent_level = level->parent_level; + FilterElt *parent_elt = level->parent_elt; + + /* we are at zero -- time to increase the zero_ref_count val */ + while (parent_level) + { + parent_elt->zero_ref_count++; + + parent_elt = parent_level->parent_elt; + parent_level = parent_level->parent_level; + } + + if (filter->priv->root != level) + filter->priv->zero_ref_count++; + +#ifdef MODEL_FILTER_DEBUG + g_assert (filter->priv->zero_ref_count >= 0); + if (filter->priv->zero_ref_count > 0) + g_assert (filter->priv->root != NULL); +#endif + } + } + +#ifdef MODEL_FILTER_DEBUG + g_assert (elt->ref_count >= elt->ext_ref_count); + g_assert (elt->ref_count >= 0); + g_assert (elt->ext_ref_count >= 0); +#endif +} + +/* TreeDragSource interface implementation */ +static gboolean +gtk_tree_model_filter_row_draggable (GtkTreeDragSource *drag_source, + GtkTreePath *path) +{ + GtkTreeModelFilter *tree_model_filter = (GtkTreeModelFilter *)drag_source; + GtkTreePath *child_path; + gboolean draggable; + + g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (drag_source), FALSE); + g_return_val_if_fail (path != NULL, FALSE); + + child_path = gtk_tree_model_filter_convert_path_to_child_path (tree_model_filter, path); + draggable = gtk_tree_drag_source_row_draggable (GTK_TREE_DRAG_SOURCE (tree_model_filter->priv->child_model), child_path); + gtk_tree_path_free (child_path); + + return draggable; +} + +static GdkContentProvider * +gtk_tree_model_filter_drag_data_get (GtkTreeDragSource *drag_source, + GtkTreePath *path) +{ + GtkTreeModelFilter *tree_model_filter = (GtkTreeModelFilter *)drag_source; + GtkTreePath *child_path; + GdkContentProvider *gotten; + + g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (drag_source), NULL); + g_return_val_if_fail (path != NULL, NULL); + + child_path = gtk_tree_model_filter_convert_path_to_child_path (tree_model_filter, path); + gotten = gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (tree_model_filter->priv->child_model), child_path); + gtk_tree_path_free (child_path); + + return gotten; +} + +static gboolean +gtk_tree_model_filter_drag_data_delete (GtkTreeDragSource *drag_source, + GtkTreePath *path) +{ + GtkTreeModelFilter *tree_model_filter = (GtkTreeModelFilter *)drag_source; + GtkTreePath *child_path; + gboolean deleted; + + g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (drag_source), FALSE); + g_return_val_if_fail (path != NULL, FALSE); + + child_path = gtk_tree_model_filter_convert_path_to_child_path (tree_model_filter, path); + deleted = gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (tree_model_filter->priv->child_model), child_path); + gtk_tree_path_free (child_path); + + return deleted; +} + +/* bits and pieces */ +static void +gtk_tree_model_filter_set_model (GtkTreeModelFilter *filter, + GtkTreeModel *child_model) +{ + g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (filter)); + + if (filter->priv->child_model) + { + g_signal_handler_disconnect (filter->priv->child_model, + filter->priv->changed_id); + g_signal_handler_disconnect (filter->priv->child_model, + filter->priv->inserted_id); + g_signal_handler_disconnect (filter->priv->child_model, + filter->priv->has_child_toggled_id); + g_signal_handler_disconnect (filter->priv->child_model, + filter->priv->deleted_id); + g_signal_handler_disconnect (filter->priv->child_model, + filter->priv->reordered_id); + + /* reset our state */ + if (filter->priv->root) + gtk_tree_model_filter_free_level (filter, filter->priv->root, + TRUE, TRUE, FALSE); + + filter->priv->root = NULL; + g_object_unref (filter->priv->child_model); + filter->priv->visible_column = -1; + + /* FIXME: do we need to destroy more here? */ + } + + filter->priv->child_model = child_model; + + if (child_model) + { + g_object_ref (filter->priv->child_model); + filter->priv->changed_id = + g_signal_connect (child_model, "row-changed", + G_CALLBACK (gtk_tree_model_filter_row_changed), + filter); + filter->priv->inserted_id = + g_signal_connect (child_model, "row-inserted", + G_CALLBACK (gtk_tree_model_filter_row_inserted), + filter); + filter->priv->has_child_toggled_id = + g_signal_connect (child_model, "row-has-child-toggled", + G_CALLBACK (gtk_tree_model_filter_row_has_child_toggled), + filter); + filter->priv->deleted_id = + g_signal_connect (child_model, "row-deleted", + G_CALLBACK (gtk_tree_model_filter_row_deleted), + filter); + filter->priv->reordered_id = + g_signal_connect (child_model, "rows-reordered", + G_CALLBACK (gtk_tree_model_filter_rows_reordered), + filter); + + filter->priv->child_flags = gtk_tree_model_get_flags (child_model); + filter->priv->stamp = g_random_int (); + } +} + +static void +gtk_tree_model_filter_ref_path (GtkTreeModelFilter *filter, + GtkTreePath *path) +{ + int len; + GtkTreePath *p; + + len = gtk_tree_path_get_depth (path); + p = gtk_tree_path_copy (path); + while (len--) + { + GtkTreeIter iter; + + gtk_tree_model_get_iter (GTK_TREE_MODEL (filter->priv->child_model), &iter, p); + gtk_tree_model_ref_node (GTK_TREE_MODEL (filter->priv->child_model), &iter); + gtk_tree_path_up (p); + } + + gtk_tree_path_free (p); +} + +static void +gtk_tree_model_filter_unref_path (GtkTreeModelFilter *filter, + GtkTreePath *path, + int depth) +{ + int len; + GtkTreePath *p; + + if (depth != -1) + len = depth; + else + len = gtk_tree_path_get_depth (path); + + p = gtk_tree_path_copy (path); + while (len--) + { + GtkTreeIter iter; + + gtk_tree_model_get_iter (GTK_TREE_MODEL (filter->priv->child_model), &iter, p); + gtk_tree_model_unref_node (GTK_TREE_MODEL (filter->priv->child_model), &iter); + gtk_tree_path_up (p); + } + + gtk_tree_path_free (p); +} + +static void +gtk_tree_model_filter_set_root (GtkTreeModelFilter *filter, + GtkTreePath *root) +{ + g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (filter)); + + if (root) + { + filter->priv->virtual_root = gtk_tree_path_copy (root); + gtk_tree_model_filter_ref_path (filter, filter->priv->virtual_root); + filter->priv->virtual_root_deleted = FALSE; + } + else + filter->priv->virtual_root = NULL; +} + +/* public API */ + +/** + * gtk_tree_model_filter_new: + * @child_model: A `GtkTreeModel`. + * @root: (nullable): A `GtkTreePath` + * + * Creates a new `GtkTreeModel`, with @child_model as the child_model + * and @root as the virtual root. + * + * Returns: (transfer full): A new `GtkTreeModel`. + * + * Deprecated: 4.10 + */ +GtkTreeModel * +gtk_tree_model_filter_new (GtkTreeModel *child_model, + GtkTreePath *root) +{ + g_return_val_if_fail (GTK_IS_TREE_MODEL (child_model), NULL); + + return g_object_new (GTK_TYPE_TREE_MODEL_FILTER, + "child-model", child_model, + "virtual-root", root, + NULL); +} + +/** + * gtk_tree_model_filter_get_model: + * @filter: A `GtkTreeModelFilter` + * + * Returns a pointer to the child model of @filter. + * + * Returns: (transfer none): A pointer to a `GtkTreeModel` + * + * Deprecated: 4.10 + */ +GtkTreeModel * +gtk_tree_model_filter_get_model (GtkTreeModelFilter *filter) +{ + g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (filter), NULL); + + return filter->priv->child_model; +} + +/** + * gtk_tree_model_filter_set_visible_func: + * @filter: A `GtkTreeModelFilter` + * @func: A `GtkTreeModelFilterVisibleFunc`, the visible function + * @data: (nullable): User data to pass to the visible function + * @destroy: (nullable): Destroy notifier of @data + * + * Sets the visible function used when filtering the @filter to be @func. + * The function should return %TRUE if the given row should be visible and + * %FALSE otherwise. + * + * If the condition calculated by the function changes over time (e.g. + * because it depends on some global parameters), you must call + * gtk_tree_model_filter_refilter() to keep the visibility information + * of the model up-to-date. + * + * Note that @func is called whenever a row is inserted, when it may still + * be empty. The visible function should therefore take special care of empty + * rows, like in the example below. + * + * |[ + * static gboolean + * visible_func (GtkTreeModel *model, + * GtkTreeIter *iter, + * gpointer data) + * { + * // Visible if row is non-empty and first column is “HI” + * char *str; + * gboolean visible = FALSE; + * + * gtk_tree_model_get (model, iter, 0, &str, -1); + * if (str && strcmp (str, "HI") == 0) + * visible = TRUE; + * g_free (str); + * + * return visible; + * } + * ]| + * + * Note that gtk_tree_model_filter_set_visible_func() or + * gtk_tree_model_filter_set_visible_column() can only be called + * once for a given filter model. + * + * Deprecated: 4.10 + */ +void +gtk_tree_model_filter_set_visible_func (GtkTreeModelFilter *filter, + GtkTreeModelFilterVisibleFunc func, + gpointer data, + GDestroyNotify destroy) +{ + g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (filter)); + g_return_if_fail (func != NULL); + g_return_if_fail (filter->priv->visible_method_set == FALSE); + + filter->priv->visible_func = func; + filter->priv->visible_data = data; + filter->priv->visible_destroy = destroy; + + filter->priv->visible_method_set = TRUE; +} + +/** + * gtk_tree_model_filter_set_modify_func: + * @filter: A `GtkTreeModelFilter` + * @n_columns: The number of columns in the filter model. + * @types: (array length=n_columns): The `GType`s of the columns. + * @func: A `GtkTreeModelFilterModifyFunc` + * @data: (nullable): User data to pass to the modify function + * @destroy: (nullable): Destroy notifier of @data + * + * With the @n_columns and @types parameters, you give an array of column + * types for this model (which will be exposed to the parent model/view). + * The @func, @data and @destroy parameters are for specifying the modify + * function. The modify function will get called for each + * data access, the goal of the modify function is to return the data which + * should be displayed at the location specified using the parameters of the + * modify function. + * + * Note that gtk_tree_model_filter_set_modify_func() + * can only be called once for a given filter model. + * + * Deprecated: 4.10 + */ +void +gtk_tree_model_filter_set_modify_func (GtkTreeModelFilter *filter, + int n_columns, + GType *types, + GtkTreeModelFilterModifyFunc func, + gpointer data, + GDestroyNotify destroy) +{ + g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (filter)); + g_return_if_fail (func != NULL); + g_return_if_fail (filter->priv->modify_func_set == FALSE); + + filter->priv->modify_n_columns = n_columns; + filter->priv->modify_types = g_new0 (GType, n_columns); + memcpy (filter->priv->modify_types, types, sizeof (GType) * n_columns); + filter->priv->modify_func = func; + filter->priv->modify_data = data; + filter->priv->modify_destroy = destroy; + + filter->priv->modify_func_set = TRUE; +} + +/** + * gtk_tree_model_filter_set_visible_column: + * @filter: A `GtkTreeModelFilter` + * @column: A `int` which is the column containing the visible information + * + * Sets @column of the child_model to be the column where @filter should + * look for visibility information. @columns should be a column of type + * %G_TYPE_BOOLEAN, where %TRUE means that a row is visible, and %FALSE + * if not. + * + * Note that gtk_tree_model_filter_set_visible_func() or + * gtk_tree_model_filter_set_visible_column() can only be called + * once for a given filter model. + * + * Deprecated: 4.10 + */ +void +gtk_tree_model_filter_set_visible_column (GtkTreeModelFilter *filter, + int column) +{ + g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (filter)); + g_return_if_fail (column >= 0); + g_return_if_fail (filter->priv->visible_method_set == FALSE); + + filter->priv->visible_column = column; + + filter->priv->visible_method_set = TRUE; +} + +/* conversion */ + +/** + * gtk_tree_model_filter_convert_child_iter_to_iter: + * @filter: A `GtkTreeModelFilter` + * @filter_iter: (out): An uninitialized `GtkTreeIter` + * @child_iter: A valid `GtkTreeIter` pointing to a row on the child model. + * + * Sets @filter_iter to point to the row in @filter that corresponds to the + * row pointed at by @child_iter. If @filter_iter was not set, %FALSE is + * returned. + * + * Returns: %TRUE, if @filter_iter was set, i.e. if @child_iter is a + * valid iterator pointing to a visible row in child model. + * + * Deprecated: 4.10 + */ +gboolean +gtk_tree_model_filter_convert_child_iter_to_iter (GtkTreeModelFilter *filter, + GtkTreeIter *filter_iter, + GtkTreeIter *child_iter) +{ + gboolean ret; + GtkTreePath *child_path, *path; + + g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (filter), FALSE); + g_return_val_if_fail (filter->priv->child_model != NULL, FALSE); + g_return_val_if_fail (filter_iter != NULL, FALSE); + g_return_val_if_fail (child_iter != NULL, FALSE); + g_return_val_if_fail (filter_iter != child_iter, FALSE); + + filter_iter->stamp = 0; + + child_path = gtk_tree_model_get_path (filter->priv->child_model, child_iter); + g_return_val_if_fail (child_path != NULL, FALSE); + + path = gtk_tree_model_filter_convert_child_path_to_path (filter, + child_path); + gtk_tree_path_free (child_path); + + if (!path) + return FALSE; + + ret = gtk_tree_model_get_iter (GTK_TREE_MODEL (filter), filter_iter, path); + gtk_tree_path_free (path); + + return ret; +} + +/** + * gtk_tree_model_filter_convert_iter_to_child_iter: + * @filter: A `GtkTreeModelFilter` + * @child_iter: (out): An uninitialized `GtkTreeIter` + * @filter_iter: A valid `GtkTreeIter` pointing to a row on @filter. + * + * Sets @child_iter to point to the row pointed to by @filter_iter. + * + * Deprecated: 4.10 + */ +void +gtk_tree_model_filter_convert_iter_to_child_iter (GtkTreeModelFilter *filter, + GtkTreeIter *child_iter, + GtkTreeIter *filter_iter) +{ + g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (filter)); + g_return_if_fail (filter->priv->child_model != NULL); + g_return_if_fail (child_iter != NULL); + g_return_if_fail (filter_iter != NULL); + g_return_if_fail (filter_iter->stamp == filter->priv->stamp); + g_return_if_fail (filter_iter != child_iter); + + if (GTK_TREE_MODEL_FILTER_CACHE_CHILD_ITERS (filter)) + { + *child_iter = FILTER_ELT (filter_iter->user_data2)->iter; + } + else + { + GtkTreePath *path; + gboolean valid = FALSE; + + path = gtk_tree_model_filter_elt_get_path (filter_iter->user_data, + filter_iter->user_data2, + filter->priv->virtual_root); + valid = gtk_tree_model_get_iter (filter->priv->child_model, child_iter, + path); + gtk_tree_path_free (path); + + g_return_if_fail (valid == TRUE); + } +} + +/* The path returned can only be used internally in the filter model. */ +static GtkTreePath * +gtk_real_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter *filter, + GtkTreePath *child_path, + gboolean build_levels, + gboolean fetch_children) +{ + int *child_indices; + GtkTreePath *retval; + GtkTreePath *real_path; + FilterLevel *level; + FilterElt *tmp; + int i; + + g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (filter), NULL); + g_return_val_if_fail (filter->priv->child_model != NULL, NULL); + g_return_val_if_fail (child_path != NULL, NULL); + + if (!filter->priv->virtual_root) + real_path = gtk_tree_path_copy (child_path); + else + real_path = gtk_tree_model_filter_remove_root (child_path, + filter->priv->virtual_root); + + if (!real_path) + return NULL; + + retval = gtk_tree_path_new (); + child_indices = gtk_tree_path_get_indices (real_path); + + if (filter->priv->root == NULL && build_levels) + gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE); + level = FILTER_LEVEL (filter->priv->root); + + for (i = 0; i < gtk_tree_path_get_depth (real_path); i++) + { + GSequenceIter *siter; + gboolean found_child = FALSE; + + if (!level) + { + gtk_tree_path_free (real_path); + gtk_tree_path_free (retval); + return NULL; + } + + tmp = lookup_elt_with_offset (level->seq, child_indices[i], &siter); + if (tmp) + { + gtk_tree_path_append_index (retval, g_sequence_iter_get_position (siter)); + if (!tmp->children && build_levels) + gtk_tree_model_filter_build_level (filter, level, tmp, FALSE); + level = tmp->children; + found_child = TRUE; + } + + if (!found_child && fetch_children) + { + int j; + + tmp = gtk_tree_model_filter_fetch_child (filter, level, + child_indices[i], + &j); + + /* didn't find the child, let's try to bring it back */ + if (!tmp || tmp->offset != child_indices[i]) + { + /* not there */ + gtk_tree_path_free (real_path); + gtk_tree_path_free (retval); + return NULL; + } + + gtk_tree_path_append_index (retval, j); + if (!tmp->children && build_levels) + gtk_tree_model_filter_build_level (filter, level, tmp, FALSE); + level = tmp->children; + found_child = TRUE; + } + else if (!found_child && !fetch_children) + { + /* no path */ + gtk_tree_path_free (real_path); + gtk_tree_path_free (retval); + return NULL; + } + } + + gtk_tree_path_free (real_path); + return retval; +} + +/** + * gtk_tree_model_filter_convert_child_path_to_path: + * @filter: A `GtkTreeModelFilter` + * @child_path: A `GtkTreePath` to convert. + * + * Converts @child_path to a path relative to @filter. That is, @child_path + * points to a path in the child model. The rerturned path will point to the + * same row in the filtered model. If @child_path isn’t a valid path on the + * child model or points to a row which is not visible in @filter, then %NULL + * is returned. + * + * Returns: (nullable) (transfer full): A newly allocated `GtkTreePath` + * + * Deprecated: 4.10 + */ +GtkTreePath * +gtk_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter *filter, + GtkTreePath *child_path) +{ + GtkTreeIter iter; + GtkTreePath *path; + + /* this function does the sanity checks */ + path = gtk_real_tree_model_filter_convert_child_path_to_path (filter, + child_path, + TRUE, + TRUE); + + if (!path) + return NULL; + + /* get a new path which only takes visible nodes into account. + * -- if this gives any performance issues, we can write a special + * version of convert_child_path_to_path immediately returning + * a visible-nodes-only path. + */ + gtk_tree_model_filter_get_iter_full (GTK_TREE_MODEL (filter), &iter, path); + + gtk_tree_path_free (path); + path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter); + + return path; +} + +/** + * gtk_tree_model_filter_convert_path_to_child_path: + * @filter: A `GtkTreeModelFilter` + * @filter_path: A `GtkTreePath` to convert. + * + * Converts @filter_path to a path on the child model of @filter. That is, + * @filter_path points to a location in @filter. The returned path will + * point to the same location in the model not being filtered. If @filter_path + * does not point to a location in the child model, %NULL is returned. + * + * Returns: (nullable) (transfer full): A newly allocated `GtkTreePath` + * + * Deprecated: 4.10 + */ +GtkTreePath * +gtk_tree_model_filter_convert_path_to_child_path (GtkTreeModelFilter *filter, + GtkTreePath *filter_path) +{ + int *filter_indices; + GtkTreePath *retval; + FilterLevel *level; + int i; + + g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (filter), NULL); + g_return_val_if_fail (filter->priv->child_model != NULL, NULL); + g_return_val_if_fail (filter_path != NULL, NULL); + + /* convert path */ + retval = gtk_tree_path_new (); + filter_indices = gtk_tree_path_get_indices (filter_path); + if (!filter->priv->root) + gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE); + level = FILTER_LEVEL (filter->priv->root); + + for (i = 0; i < gtk_tree_path_get_depth (filter_path); i++) + { + FilterElt *elt; + GSequenceIter *siter; + + if (!level) + { + gtk_tree_path_free (retval); + return NULL; + } + + siter = g_sequence_get_iter_at_pos (level->visible_seq, filter_indices[i]); + if (g_sequence_iter_is_end (siter)) + { + gtk_tree_path_free (retval); + return NULL; + } + + elt = GET_ELT (siter); + g_assert (elt); + if (elt->children == NULL) + gtk_tree_model_filter_build_level (filter, level, elt, FALSE); + + gtk_tree_path_append_index (retval, elt->offset); + level = elt->children; + } + + /* apply vroot */ + + if (filter->priv->virtual_root) + { + GtkTreePath *real_retval; + + real_retval = gtk_tree_model_filter_add_root (retval, + filter->priv->virtual_root); + gtk_tree_path_free (retval); + + return real_retval; + } + + return retval; +} + +static gboolean +gtk_tree_model_filter_refilter_helper (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + /* evil, don't try this at home, but certainly speeds things up */ + gtk_tree_model_filter_row_changed (model, path, iter, data); + + return FALSE; +} + +/** + * gtk_tree_model_filter_refilter: + * @filter: A `GtkTreeModelFilter` + * + * Emits ::row_changed for each row in the child model, which causes + * the filter to re-evaluate whether a row is visible or not. + * + * Deprecated: 4.10 + */ +void +gtk_tree_model_filter_refilter (GtkTreeModelFilter *filter) +{ + g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (filter)); + + /* S L O W */ + gtk_tree_model_foreach (filter->priv->child_model, + gtk_tree_model_filter_refilter_helper, + filter); +} + +/** + * gtk_tree_model_filter_clear_cache: + * @filter: A `GtkTreeModelFilter` + * + * This function should almost never be called. It clears the @filter + * of any cached iterators that haven’t been reffed with + * gtk_tree_model_ref_node(). This might be useful if the child model + * being filtered is static (and doesn’t change often) and there has been + * a lot of unreffed access to nodes. As a side effect of this function, + * all unreffed iters will be invalid. + * + * Deprecated: 4.10 + */ +void +gtk_tree_model_filter_clear_cache (GtkTreeModelFilter *filter) +{ + g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (filter)); + + if (filter->priv->zero_ref_count > 0) + gtk_tree_model_filter_clear_cache_helper (filter, + FILTER_LEVEL (filter->priv->root)); +} diff --git a/gtk/deprecated/gtktreemodelfilter.h b/gtk/deprecated/gtktreemodelfilter.h new file mode 100644 index 0000000000..ec6510b7d8 --- /dev/null +++ b/gtk/deprecated/gtktreemodelfilter.h @@ -0,0 +1,156 @@ +/* gtktreemodelfilter.h + * Copyright (C) 2000,2001 Red Hat, Inc., Jonathan Blandford + * Copyright (C) 2001-2003 Kristian Rietveld + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#ifndef __GTK_TREE_MODEL_FILTER_H__ +#define __GTK_TREE_MODEL_FILTER_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_TREE_MODEL_FILTER (gtk_tree_model_filter_get_type ()) +#define GTK_TREE_MODEL_FILTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_TREE_MODEL_FILTER, GtkTreeModelFilter)) +#define GTK_TREE_MODEL_FILTER_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST ((vtable), GTK_TYPE_TREE_MODEL_FILTER, GtkTreeModelFilterClass)) +#define GTK_IS_TREE_MODEL_FILTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_TREE_MODEL_FILTER)) +#define GTK_IS_TREE_MODEL_FILTER_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), GTK_TYPE_TREE_MODEL_FILTER)) +#define GTK_TREE_MODEL_FILTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_TREE_MODEL_FILTER, GtkTreeModelFilterClass)) + +/** + * GtkTreeModelFilterVisibleFunc: + * @model: the child model of the `GtkTreeModelFilter` + * @iter: a `GtkTreeIter` pointing to the row in @model whose visibility + * is determined + * @data: (closure): user data given to gtk_tree_model_filter_set_visible_func() + * + * A function which decides whether the row indicated by @iter is visible. + * + * Returns: Whether the row indicated by @iter is visible. + */ +typedef gboolean (* GtkTreeModelFilterVisibleFunc) (GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data); + +/** + * GtkTreeModelFilterModifyFunc: + * @model: the `GtkTreeModelFilter` + * @iter: a `GtkTreeIter` pointing to the row whose display values are determined + * @value: (out caller-allocates): A `GValue` which is already initialized for + * with the correct type for the column @column. + * @column: the column whose display value is determined + * @data: (closure): user data given to gtk_tree_model_filter_set_modify_func() + * + * A function which calculates display values from raw values in the model. + * It must fill @value with the display value for the column @column in the + * row indicated by @iter. + * + * Since this function is called for each data access, it’s not a + * particularly efficient operation. + */ + +typedef void (* GtkTreeModelFilterModifyFunc) (GtkTreeModel *model, + GtkTreeIter *iter, + GValue *value, + int column, + gpointer data); + +typedef struct _GtkTreeModelFilter GtkTreeModelFilter; +typedef struct _GtkTreeModelFilterClass GtkTreeModelFilterClass; +typedef struct _GtkTreeModelFilterPrivate GtkTreeModelFilterPrivate; + +struct _GtkTreeModelFilter +{ + GObject parent; + + /*< private >*/ + GtkTreeModelFilterPrivate *priv; +}; + +struct _GtkTreeModelFilterClass +{ + GObjectClass parent_class; + + gboolean (* visible) (GtkTreeModelFilter *self, + GtkTreeModel *child_model, + GtkTreeIter *iter); + void (* modify) (GtkTreeModelFilter *self, + GtkTreeModel *child_model, + GtkTreeIter *iter, + GValue *value, + int column); + + /*< private >*/ + + gpointer padding[8]; +}; + +/* base */ +GDK_AVAILABLE_IN_ALL +GType gtk_tree_model_filter_get_type (void) G_GNUC_CONST; +GDK_DEPRECATED_IN_4_10 +GtkTreeModel *gtk_tree_model_filter_new (GtkTreeModel *child_model, + GtkTreePath *root); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_model_filter_set_visible_func (GtkTreeModelFilter *filter, + GtkTreeModelFilterVisibleFunc func, + gpointer data, + GDestroyNotify destroy); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_model_filter_set_modify_func (GtkTreeModelFilter *filter, + int n_columns, + GType *types, + GtkTreeModelFilterModifyFunc func, + gpointer data, + GDestroyNotify destroy); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_model_filter_set_visible_column (GtkTreeModelFilter *filter, + int column); + +GDK_DEPRECATED_IN_4_10 +GtkTreeModel *gtk_tree_model_filter_get_model (GtkTreeModelFilter *filter); + +/* conversion */ +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_model_filter_convert_child_iter_to_iter (GtkTreeModelFilter *filter, + GtkTreeIter *filter_iter, + GtkTreeIter *child_iter); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_model_filter_convert_iter_to_child_iter (GtkTreeModelFilter *filter, + GtkTreeIter *child_iter, + GtkTreeIter *filter_iter); +GDK_DEPRECATED_IN_4_10 +GtkTreePath *gtk_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter *filter, + GtkTreePath *child_path); +GDK_DEPRECATED_IN_4_10 +GtkTreePath *gtk_tree_model_filter_convert_path_to_child_path (GtkTreeModelFilter *filter, + GtkTreePath *filter_path); + +/* extras */ +GDK_DEPRECATED_IN_4_10 +void gtk_tree_model_filter_refilter (GtkTreeModelFilter *filter); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_model_filter_clear_cache (GtkTreeModelFilter *filter); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkTreeModelFilter, g_object_unref) + +G_END_DECLS + +#endif /* __GTK_TREE_MODEL_FILTER_H__ */ diff --git a/gtk/deprecated/gtktreemodelsort.c b/gtk/deprecated/gtktreemodelsort.c new file mode 100644 index 0000000000..f6da951bed --- /dev/null +++ b/gtk/deprecated/gtktreemodelsort.c @@ -0,0 +1,2796 @@ +/* gtktreemodelsort.c + * Copyright (C) 2000,2001 Red Hat, Inc., Jonathan Blandford + * Copyright (C) 2001,2002 Kristian Rietveld + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#include "config.h" +#include + +#include "gtktreemodelsort.h" +#include "gtktreesortable.h" +#include "gtktreestore.h" +#include "gtktreedatalistprivate.h" +#include "gtkprivate.h" +#include "gtktreednd.h" + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + +/** + * GtkTreeModelSort: + * + * A GtkTreeModel which makes an underlying tree model sortable + * + * The `GtkTreeModelSort` is a model which implements the `GtkTreeSortable` + * interface. It does not hold any data itself, but rather is created with + * a child model and proxies its data. It has identical column types to + * this child model, and the changes in the child are propagated. The + * primary purpose of this model is to provide a way to sort a different + * model without modifying it. Note that the sort function used by + * `GtkTreeModelSort` is not guaranteed to be stable. + * + * The use of this is best demonstrated through an example. In the + * following sample code we create two `GtkTreeView` widgets each with a + * view of the same data. As the model is wrapped here by a + * `GtkTreeModelSort`, the two `GtkTreeView`s can each sort their + * view of the data without affecting the other. By contrast, if we + * simply put the same model in each widget, then sorting the first would + * sort the second. + * + * ## Using a `GtkTreeModelSort` + * + * |[ + * { + * GtkTreeView *tree_view1; + * GtkTreeView *tree_view2; + * GtkTreeModel *sort_model1; + * GtkTreeModel *sort_model2; + * GtkTreeModel *child_model; + * + * // get the child model + * child_model = get_my_model (); + * + * // Create the first tree + * sort_model1 = gtk_tree_model_sort_new_with_model (child_model); + * tree_view1 = gtk_tree_view_new_with_model (sort_model1); + * + * // Create the second tree + * sort_model2 = gtk_tree_model_sort_new_with_model (child_model); + * tree_view2 = gtk_tree_view_new_with_model (sort_model2); + * + * // Now we can sort the two models independently + * gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort_model1), + * COLUMN_1, GTK_SORT_ASCENDING); + * gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort_model2), + * COLUMN_1, GTK_SORT_DESCENDING); + * } + * ]| + * + * To demonstrate how to access the underlying child model from the sort + * model, the next example will be a callback for the `GtkTreeSelection` + * `GtkTreeSelection::changed` signal. In this callback, we get a string + * from COLUMN_1 of the model. We then modify the string, find the same + * selected row on the child model, and change the row there. + * + * ## Accessing the child model of in a selection changed callback + * + * |[ + * void + * selection_changed (GtkTreeSelection *selection, gpointer data) + * { + * GtkTreeModel *sort_model = NULL; + * GtkTreeModel *child_model; + * GtkTreeIter sort_iter; + * GtkTreeIter child_iter; + * char *some_data = NULL; + * char *modified_data; + * + * // Get the current selected row and the model. + * if (! gtk_tree_selection_get_selected (selection, + * &sort_model, + * &sort_iter)) + * return; + * + * // Look up the current value on the selected row and get + * // a new value to change it to. + * gtk_tree_model_get (GTK_TREE_MODEL (sort_model), &sort_iter, + * COLUMN_1, &some_data, + * -1); + * + * modified_data = change_the_data (some_data); + * g_free (some_data); + * + * // Get an iterator on the child model, instead of the sort model. + * gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (sort_model), + * &child_iter, + * &sort_iter); + * + * // Get the child model and change the value of the row. In this + * // example, the child model is a GtkListStore. It could be any other + * // type of model, though. + * child_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model)); + * gtk_list_store_set (GTK_LIST_STORE (child_model), &child_iter, + * COLUMN_1, &modified_data, + * -1); + * g_free (modified_data); + * } + * ]| + */ + + +/* Notes on this implementation of GtkTreeModelSort + * ================================================ + * + * Warnings + * -------- + * + * In this code there is a potential for confusion as to whether an iter, + * path or value refers to the GtkTreeModelSort model, or to the child model + * that has been set. As a convention, variables referencing the child model + * will have an s_ prefix before them (ie. s_iter, s_value, s_path); + * Conversion of iterators and paths between GtkTreeModelSort and the child + * model is done through the various gtk_tree_model_sort_convert_* functions. + * + * Iterator format + * --------------- + * + * The iterator format of iterators handed out by GtkTreeModelSort is as + * follows: + * + * iter->stamp = tree_model_sort->stamp + * iter->user_data = SortLevel + * iter->user_data2 = SortElt + * + * Internal data structure + * ----------------------- + * + * Using SortLevel and SortElt, GtkTreeModelSort maintains a “cache” of + * the mapping from GtkTreeModelSort nodes to nodes in the child model. + * This is to avoid sorting a level each time an operation is requested + * on GtkTreeModelSort, such as get iter, get path, get value. + * + * A SortElt corresponds to a single node. A node and its siblings are + * stored in a SortLevel. The SortLevel keeps a reference to the parent + * SortElt and its SortLevel (if any). The SortElt can have a "children" + * pointer set, which points at a child level (a sub level). + * + * In a SortLevel, nodes are stored in a GSequence. The GSequence + * allows for fast additions and removals, and supports sorting + * the level of SortElt nodes. + * + * It is important to recognize the two different mappings that play + * a part in this code: + * I. The mapping from the client to this model. The order in which + * nodes are stored in the GSequence is the order in which the + * nodes are exposed to clients of the GtkTreeModelSort. + * II. The mapping from this model to its child model. Each SortElt + * contains an “offset” field which is the offset of the + * corresponding node in the child model. + * + * Reference counting + * ------------------ + * + * GtkTreeModelSort forwards all reference and unreference operations + * to the corresponding node in the child model. The reference count + * of each node is also maintained internally, in the “ref_count” + * fields in SortElt and SortLevel. For each ref and unref operation on + * a SortElt, the “ref_count” of the SortLevel is updated accordingly. + * In addition, if a SortLevel has a parent, a reference is taken on + * this parent. This happens in gtk_tree_model_sort_build_level() and + * the reference is released again in gtk_tree_model_sort_free_level(). + * This ensures that when GtkTreeModelSort takes a reference on a node + * (for example during sorting), all parent nodes are referenced + * according to reference counting rule 1, see the GtkTreeModel + * documentation. + * + * When a level has a reference count of zero, which means that + * none of the nodes in the level is referenced, the level has + * a “zero ref count” on all its parents. As soon as the level + * reaches a reference count of zero, the zero ref count value is + * incremented by one on all parents of this level. Similarly, as + * soon as the reference count of a level changes from zero, the + * zero ref count value is decremented by one on all parents. + * + * The zero ref count value is used to clear unused portions of + * the cache. If a SortElt has a zero ref count of one, then + * its child level is unused and can be removed from the cache. + * If the zero ref count value is higher than one, then the + * child level contains sublevels which are unused as well. + * gtk_tree_model_sort_clear_cache() uses this to not recurse + * into levels which have a zero ref count of zero. + */ + +typedef struct _SortElt SortElt; +typedef struct _SortLevel SortLevel; +typedef struct _SortData SortData; + +struct _SortElt +{ + GtkTreeIter iter; + SortLevel *children; + int offset; + int ref_count; + int zero_ref_count; + int old_index; /* used while sorting */ + GSequenceIter *siter; /* iter into seq */ +}; + +struct _SortLevel +{ + GSequence *seq; + int ref_count; + SortElt *parent_elt; + SortLevel *parent_level; +}; + +struct _SortData +{ + GtkTreeModelSort *tree_model_sort; + GtkTreeIterCompareFunc sort_func; + gpointer sort_data; + + GtkTreePath *parent_path; + int *parent_path_indices; + int parent_path_depth; +}; + +/* Properties */ +enum { + PROP_0, + /* Construct args */ + PROP_MODEL +}; + + +struct _GtkTreeModelSortPrivate +{ + gpointer root; + int stamp; + guint child_flags; + GtkTreeModel *child_model; + int zero_ref_count; + + /* sort information */ + GList *sort_list; + int sort_column_id; + GtkSortType order; + + /* default sort */ + GtkTreeIterCompareFunc default_sort_func; + gpointer default_sort_data; + GDestroyNotify default_sort_destroy; + + /* signal ids */ + gulong changed_id; + gulong inserted_id; + gulong has_child_toggled_id; + gulong deleted_id; + gulong reordered_id; +}; + +/* Set this to 0 to disable caching of child iterators. This + * allows for more stringent testing. It is recommended to set this + * to one when refactoring this code and running the unit tests to + * catch more errors. + */ +#if 1 +# define GTK_TREE_MODEL_SORT_CACHE_CHILD_ITERS(tree_model_sort) \ + (((GtkTreeModelSort *)tree_model_sort)->priv->child_flags>K_TREE_MODEL_ITERS_PERSIST) +#else +# define GTK_TREE_MODEL_SORT_CACHE_CHILD_ITERS(tree_model_sort) (FALSE) +#endif + +#define SORT_ELT(sort_elt) ((SortElt *)sort_elt) +#define SORT_LEVEL(sort_level) ((SortLevel *)sort_level) +#define GET_ELT(siter) ((SortElt *) (siter ? g_sequence_get (siter) : NULL)) + + +#define GET_CHILD_ITER(tree_model_sort,ch_iter,so_iter) gtk_tree_model_sort_convert_iter_to_child_iter((GtkTreeModelSort*)(tree_model_sort), (ch_iter), (so_iter)); + +#define NO_SORT_FUNC ((GtkTreeIterCompareFunc) 0x1) + +#define VALID_ITER(iter, tree_model_sort) ((iter) != NULL && (iter)->user_data != NULL && (iter)->user_data2 != NULL && (tree_model_sort)->priv->stamp == (iter)->stamp) + +/* general (object/interface init, etc) */ +static void gtk_tree_model_sort_tree_model_init (GtkTreeModelIface *iface); +static void gtk_tree_model_sort_tree_sortable_init (GtkTreeSortableIface *iface); +static void gtk_tree_model_sort_drag_source_init (GtkTreeDragSourceIface*iface); +static void gtk_tree_model_sort_finalize (GObject *object); +static void gtk_tree_model_sort_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gtk_tree_model_sort_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); + +/* our signal handlers */ +static void gtk_tree_model_sort_row_changed (GtkTreeModel *model, + GtkTreePath *start_path, + GtkTreeIter *start_iter, + gpointer data); +static void gtk_tree_model_sort_row_inserted (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data); +static void gtk_tree_model_sort_row_has_child_toggled (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data); +static void gtk_tree_model_sort_row_deleted (GtkTreeModel *model, + GtkTreePath *path, + gpointer data); +static void gtk_tree_model_sort_rows_reordered (GtkTreeModel *s_model, + GtkTreePath *s_path, + GtkTreeIter *s_iter, + int *new_order, + gpointer data); + +/* TreeModel interface */ +static GtkTreeModelFlags gtk_tree_model_sort_get_flags (GtkTreeModel *tree_model); +static int gtk_tree_model_sort_get_n_columns (GtkTreeModel *tree_model); +static GType gtk_tree_model_sort_get_column_type (GtkTreeModel *tree_model, + int index); +static gboolean gtk_tree_model_sort_get_iter (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreePath *path); +static GtkTreePath *gtk_tree_model_sort_get_path (GtkTreeModel *tree_model, + GtkTreeIter *iter); +static void gtk_tree_model_sort_get_value (GtkTreeModel *tree_model, + GtkTreeIter *iter, + int column, + GValue *value); +static gboolean gtk_tree_model_sort_iter_next (GtkTreeModel *tree_model, + GtkTreeIter *iter); +static gboolean gtk_tree_model_sort_iter_previous (GtkTreeModel *tree_model, + GtkTreeIter *iter); +static gboolean gtk_tree_model_sort_iter_children (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent); +static gboolean gtk_tree_model_sort_iter_has_child (GtkTreeModel *tree_model, + GtkTreeIter *iter); +static int gtk_tree_model_sort_iter_n_children (GtkTreeModel *tree_model, + GtkTreeIter *iter); +static gboolean gtk_tree_model_sort_iter_nth_child (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent, + int n); +static gboolean gtk_tree_model_sort_iter_parent (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *child); +static void gtk_tree_model_sort_ref_node (GtkTreeModel *tree_model, + GtkTreeIter *iter); +static void gtk_tree_model_sort_real_unref_node (GtkTreeModel *tree_model, + GtkTreeIter *iter, + gboolean propagate_unref); +static void gtk_tree_model_sort_unref_node (GtkTreeModel *tree_model, + GtkTreeIter *iter); + +/* TreeDragSource interface */ +static gboolean gtk_tree_model_sort_row_draggable (GtkTreeDragSource *drag_source, + GtkTreePath *path); +static GdkContentProvider * + gtk_tree_model_sort_drag_data_get (GtkTreeDragSource *drag_source, + GtkTreePath *path); +static gboolean gtk_tree_model_sort_drag_data_delete (GtkTreeDragSource *drag_source, + GtkTreePath *path); + +/* TreeSortable interface */ +static gboolean gtk_tree_model_sort_get_sort_column_id (GtkTreeSortable *sortable, + int *sort_column_id, + GtkSortType *order); +static void gtk_tree_model_sort_set_sort_column_id (GtkTreeSortable *sortable, + int sort_column_id, + GtkSortType order); +static void gtk_tree_model_sort_set_sort_func (GtkTreeSortable *sortable, + int sort_column_id, + GtkTreeIterCompareFunc func, + gpointer data, + GDestroyNotify destroy); +static void gtk_tree_model_sort_set_default_sort_func (GtkTreeSortable *sortable, + GtkTreeIterCompareFunc func, + gpointer data, + GDestroyNotify destroy); +static gboolean gtk_tree_model_sort_has_default_sort_func (GtkTreeSortable *sortable); + +/* Private functions (sort funcs, level handling and other utils) */ +static void gtk_tree_model_sort_build_level (GtkTreeModelSort *tree_model_sort, + SortLevel *parent_level, + SortElt *parent_elt); +static void gtk_tree_model_sort_free_level (GtkTreeModelSort *tree_model_sort, + SortLevel *sort_level, + gboolean unref); +static void gtk_tree_model_sort_increment_stamp (GtkTreeModelSort *tree_model_sort); +static void gtk_tree_model_sort_sort_level (GtkTreeModelSort *tree_model_sort, + SortLevel *level, + gboolean recurse, + gboolean emit_reordered); +static void gtk_tree_model_sort_sort (GtkTreeModelSort *tree_model_sort); +static gboolean gtk_tree_model_sort_insert_value (GtkTreeModelSort *tree_model_sort, + SortLevel *level, + GtkTreePath *s_path, + GtkTreeIter *s_iter); +static GtkTreePath *gtk_tree_model_sort_elt_get_path (SortLevel *level, + SortElt *elt); +static void gtk_tree_model_sort_set_model (GtkTreeModelSort *tree_model_sort, + GtkTreeModel *child_model); +static GtkTreePath *gtk_real_tree_model_sort_convert_child_path_to_path (GtkTreeModelSort *tree_model_sort, + GtkTreePath *child_path, + gboolean build_levels); + +static int gtk_tree_model_sort_compare_func (gconstpointer a, + gconstpointer b, + gpointer user_data); +static int gtk_tree_model_sort_offset_compare_func (gconstpointer a, + gconstpointer b, + gpointer user_data); +static void gtk_tree_model_sort_clear_cache_helper (GtkTreeModelSort *tree_model_sort, + SortLevel *level); + + +G_DEFINE_TYPE_WITH_CODE (GtkTreeModelSort, gtk_tree_model_sort, G_TYPE_OBJECT, + G_ADD_PRIVATE (GtkTreeModelSort) + G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, + gtk_tree_model_sort_tree_model_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_SORTABLE, + gtk_tree_model_sort_tree_sortable_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE, + gtk_tree_model_sort_drag_source_init)) + +static void +gtk_tree_model_sort_init (GtkTreeModelSort *tree_model_sort) +{ + GtkTreeModelSortPrivate *priv; + + tree_model_sort->priv = priv = + gtk_tree_model_sort_get_instance_private (tree_model_sort); + priv->sort_column_id = GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID; + priv->stamp = 0; + priv->zero_ref_count = 0; + priv->root = NULL; + priv->sort_list = NULL; +} + +static void +gtk_tree_model_sort_class_init (GtkTreeModelSortClass *class) +{ + GObjectClass *object_class; + + object_class = (GObjectClass *) class; + + object_class->set_property = gtk_tree_model_sort_set_property; + object_class->get_property = gtk_tree_model_sort_get_property; + + object_class->finalize = gtk_tree_model_sort_finalize; + + /* Properties */ + g_object_class_install_property (object_class, + PROP_MODEL, + g_param_spec_object ("model", NULL, NULL, + GTK_TYPE_TREE_MODEL, + GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); +} + +static void +gtk_tree_model_sort_tree_model_init (GtkTreeModelIface *iface) +{ + iface->get_flags = gtk_tree_model_sort_get_flags; + iface->get_n_columns = gtk_tree_model_sort_get_n_columns; + iface->get_column_type = gtk_tree_model_sort_get_column_type; + iface->get_iter = gtk_tree_model_sort_get_iter; + iface->get_path = gtk_tree_model_sort_get_path; + iface->get_value = gtk_tree_model_sort_get_value; + iface->iter_next = gtk_tree_model_sort_iter_next; + iface->iter_previous = gtk_tree_model_sort_iter_previous; + iface->iter_children = gtk_tree_model_sort_iter_children; + iface->iter_has_child = gtk_tree_model_sort_iter_has_child; + iface->iter_n_children = gtk_tree_model_sort_iter_n_children; + iface->iter_nth_child = gtk_tree_model_sort_iter_nth_child; + iface->iter_parent = gtk_tree_model_sort_iter_parent; + iface->ref_node = gtk_tree_model_sort_ref_node; + iface->unref_node = gtk_tree_model_sort_unref_node; +} + +static void +gtk_tree_model_sort_tree_sortable_init (GtkTreeSortableIface *iface) +{ + iface->get_sort_column_id = gtk_tree_model_sort_get_sort_column_id; + iface->set_sort_column_id = gtk_tree_model_sort_set_sort_column_id; + iface->set_sort_func = gtk_tree_model_sort_set_sort_func; + iface->set_default_sort_func = gtk_tree_model_sort_set_default_sort_func; + iface->has_default_sort_func = gtk_tree_model_sort_has_default_sort_func; +} + +static void +gtk_tree_model_sort_drag_source_init (GtkTreeDragSourceIface *iface) +{ + iface->row_draggable = gtk_tree_model_sort_row_draggable; + iface->drag_data_delete = gtk_tree_model_sort_drag_data_delete; + iface->drag_data_get = gtk_tree_model_sort_drag_data_get; +} + +/** + * gtk_tree_model_sort_new_with_model: (constructor) + * @child_model: A `GtkTreeModel` + * + * Creates a new `GtkTreeModelSort`, with @child_model as the child model. + * + * Returns: (transfer full) (type Gtk.TreeModelSort): A new `GtkTreeModelSort`. + */ +GtkTreeModel * +gtk_tree_model_sort_new_with_model (GtkTreeModel *child_model) +{ + GtkTreeModel *retval; + + g_return_val_if_fail (GTK_IS_TREE_MODEL (child_model), NULL); + + retval = g_object_new (gtk_tree_model_sort_get_type (), NULL); + + gtk_tree_model_sort_set_model (GTK_TREE_MODEL_SORT (retval), child_model); + + return retval; +} + +/* GObject callbacks */ +static void +gtk_tree_model_sort_finalize (GObject *object) +{ + GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) object; + GtkTreeModelSortPrivate *priv = tree_model_sort->priv; + + gtk_tree_model_sort_set_model (tree_model_sort, NULL); + + if (priv->root) + gtk_tree_model_sort_free_level (tree_model_sort, priv->root, TRUE); + + if (priv->sort_list) + { + _gtk_tree_data_list_header_free (priv->sort_list); + priv->sort_list = NULL; + } + + if (priv->default_sort_destroy) + { + priv->default_sort_destroy (priv->default_sort_data); + priv->default_sort_destroy = NULL; + priv->default_sort_data = NULL; + } + + + /* must chain up */ + G_OBJECT_CLASS (gtk_tree_model_sort_parent_class)->finalize (object); +} + +static void +gtk_tree_model_sort_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkTreeModelSort *tree_model_sort = GTK_TREE_MODEL_SORT (object); + + switch (prop_id) + { + case PROP_MODEL: + gtk_tree_model_sort_set_model (tree_model_sort, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_tree_model_sort_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkTreeModelSort *tree_model_sort = GTK_TREE_MODEL_SORT (object); + + switch (prop_id) + { + case PROP_MODEL: + g_value_set_object (value, gtk_tree_model_sort_get_model (tree_model_sort)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +/* helpers */ +static SortElt * +sort_elt_new (void) +{ + return g_slice_new (SortElt); +} + +static void +sort_elt_free (gpointer elt) +{ + g_slice_free (SortElt, elt); +} + +static void +increase_offset_iter (gpointer data, + gpointer user_data) +{ + SortElt *elt = data; + int offset = GPOINTER_TO_INT (user_data); + + if (elt->offset >= offset) + elt->offset++; +} + +static void +decrease_offset_iter (gpointer data, + gpointer user_data) +{ + SortElt *elt = data; + int offset = GPOINTER_TO_INT (user_data); + + if (elt->offset > offset) + elt->offset--; +} + +static void +fill_sort_data (SortData *data, + GtkTreeModelSort *tree_model_sort, + SortLevel *level) +{ + GtkTreeModelSortPrivate *priv = tree_model_sort->priv; + + data->tree_model_sort = tree_model_sort; + + if (priv->sort_column_id != GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID) + { + GtkTreeDataSortHeader *header; + + header = _gtk_tree_data_list_get_header (priv->sort_list, + priv->sort_column_id); + + g_return_if_fail (header != NULL); + g_return_if_fail (header->func != NULL); + + data->sort_func = header->func; + data->sort_data = header->data; + } + else + { + /* absolutely SHOULD NOT happen: */ + g_return_if_fail (priv->default_sort_func != NULL); + + data->sort_func = priv->default_sort_func; + data->sort_data = priv->default_sort_data; + } + + if (level->parent_elt) + { + data->parent_path = gtk_tree_model_sort_elt_get_path (level->parent_level, + level->parent_elt); + gtk_tree_path_append_index (data->parent_path, 0); + } + else + { + data->parent_path = gtk_tree_path_new_first (); + } + data->parent_path_depth = gtk_tree_path_get_depth (data->parent_path); + data->parent_path_indices = gtk_tree_path_get_indices (data->parent_path); +} + +static void +free_sort_data (SortData *data) +{ + gtk_tree_path_free (data->parent_path); +} + +static SortElt * +lookup_elt_with_offset (GtkTreeModelSort *tree_model_sort, + SortLevel *level, + int offset, + GSequenceIter **ret_siter) +{ + GSequenceIter *siter, *end_siter; + + /* FIXME: We have to do a search like this, because the sequence is not + * (always) sorted on offset order. Perhaps we should introduce a + * second sequence which is sorted on offset order. + */ + end_siter = g_sequence_get_end_iter (level->seq); + for (siter = g_sequence_get_begin_iter (level->seq); + siter != end_siter; + siter = g_sequence_iter_next (siter)) + { + SortElt *elt = g_sequence_get (siter); + + if (elt->offset == offset) + break; + } + + if (ret_siter) + *ret_siter = siter; + + return GET_ELT (siter); +} + + +static void +gtk_tree_model_sort_row_changed (GtkTreeModel *s_model, + GtkTreePath *start_s_path, + GtkTreeIter *start_s_iter, + gpointer data) +{ + GtkTreeModelSort *tree_model_sort = GTK_TREE_MODEL_SORT (data); + GtkTreeModelSortPrivate *priv = tree_model_sort->priv; + GtkTreePath *path = NULL; + GtkTreeIter iter; + GtkTreeIter tmpiter; + + SortElt *elt; + SortLevel *level; + SortData sort_data; + + gboolean free_s_path = FALSE; + + int index = 0, old_index; + + g_return_if_fail (start_s_path != NULL || start_s_iter != NULL); + + if (!start_s_path) + { + free_s_path = TRUE; + start_s_path = gtk_tree_model_get_path (s_model, start_s_iter); + } + + path = gtk_real_tree_model_sort_convert_child_path_to_path (tree_model_sort, + start_s_path, + FALSE); + if (!path) + { + if (free_s_path) + gtk_tree_path_free (start_s_path); + return; + } + + gtk_tree_model_get_iter (GTK_TREE_MODEL (data), &iter, path); + gtk_tree_model_sort_ref_node (GTK_TREE_MODEL (data), &iter); + + level = iter.user_data; + elt = iter.user_data2; + + if (g_sequence_get_length (level->seq) < 2 || + (priv->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID && + priv->default_sort_func == NO_SORT_FUNC)) + { + if (free_s_path) + gtk_tree_path_free (start_s_path); + + gtk_tree_model_row_changed (GTK_TREE_MODEL (data), path, &iter); + gtk_tree_model_sort_unref_node (GTK_TREE_MODEL (data), &iter); + + gtk_tree_path_free (path); + + return; + } + + if (GTK_TREE_MODEL_SORT_CACHE_CHILD_ITERS (tree_model_sort)) + tmpiter = elt->iter; + else + gtk_tree_model_get_iter (priv->child_model, + &tmpiter, start_s_path); + + old_index = g_sequence_iter_get_position (elt->siter); + + fill_sort_data (&sort_data, tree_model_sort, level); + g_sequence_sort_changed (elt->siter, + gtk_tree_model_sort_compare_func, + &sort_data); + free_sort_data (&sort_data); + + index = g_sequence_iter_get_position (elt->siter); + + /* Prepare the path for signal emission */ + gtk_tree_path_up (path); + gtk_tree_path_append_index (path, index); + + gtk_tree_model_sort_increment_stamp (tree_model_sort); + + /* if the item moved, then emit rows_reordered */ + if (old_index != index) + { + int *new_order; + int j; + + GtkTreePath *tmppath; + + new_order = g_new (int, g_sequence_get_length (level->seq)); + + for (j = 0; j < g_sequence_get_length (level->seq); j++) + { + if (index > old_index) + { + if (j == index) + new_order[j] = old_index; + else if (j >= old_index && j < index) + new_order[j] = j + 1; + else + new_order[j] = j; + } + else if (index < old_index) + { + if (j == index) + new_order[j] = old_index; + else if (j > index && j <= old_index) + new_order[j] = j - 1; + else + new_order[j] = j; + } + /* else? shouldn't really happen */ + } + + if (level->parent_elt) + { + iter.stamp = priv->stamp; + iter.user_data = level->parent_level; + iter.user_data2 = level->parent_elt; + + tmppath = gtk_tree_model_get_path (GTK_TREE_MODEL (tree_model_sort), &iter); + + gtk_tree_model_rows_reordered (GTK_TREE_MODEL (tree_model_sort), + tmppath, &iter, new_order); + } + else + { + /* toplevel */ + tmppath = gtk_tree_path_new (); + + gtk_tree_model_rows_reordered (GTK_TREE_MODEL (tree_model_sort), tmppath, + NULL, new_order); + } + + gtk_tree_path_free (tmppath); + g_free (new_order); + } + + /* emit row_changed signal (at new location) */ + gtk_tree_model_get_iter (GTK_TREE_MODEL (data), &iter, path); + gtk_tree_model_row_changed (GTK_TREE_MODEL (data), path, &iter); + gtk_tree_model_sort_unref_node (GTK_TREE_MODEL (data), &iter); + + gtk_tree_path_free (path); + if (free_s_path) + gtk_tree_path_free (start_s_path); +} + +static void +gtk_tree_model_sort_row_inserted (GtkTreeModel *s_model, + GtkTreePath *s_path, + GtkTreeIter *s_iter, + gpointer data) +{ + GtkTreeModelSort *tree_model_sort = GTK_TREE_MODEL_SORT (data); + GtkTreeModelSortPrivate *priv = tree_model_sort->priv; + GtkTreePath *path; + GtkTreeIter iter; + GtkTreeIter real_s_iter; + + int i = 0; + + gboolean free_s_path = FALSE; + + SortElt *elt; + SortLevel *level; + SortLevel *parent_level = NULL; + + parent_level = level = SORT_LEVEL (priv->root); + + g_return_if_fail (s_path != NULL || s_iter != NULL); + + if (!s_path) + { + s_path = gtk_tree_model_get_path (s_model, s_iter); + free_s_path = TRUE; + } + + if (!s_iter) + gtk_tree_model_get_iter (s_model, &real_s_iter, s_path); + else + real_s_iter = *s_iter; + + if (!priv->root) + { + gtk_tree_model_sort_build_level (tree_model_sort, NULL, NULL); + + /* the build level already put the inserted iter in the level, + so no need to handle this signal anymore */ + + goto done_and_submit; + } + + /* find the parent level */ + while (i < gtk_tree_path_get_depth (s_path) - 1) + { + if (!level) + { + /* level not yet build, we won't cover this signal */ + goto done; + } + + if (g_sequence_get_length (level->seq) < gtk_tree_path_get_indices (s_path)[i]) + { + g_warning ("%s: A node was inserted with a parent that's not in the tree.\n" + "This possibly means that a GtkTreeModel inserted a child node\n" + "before the parent was inserted.", + G_STRLOC); + goto done; + } + + elt = lookup_elt_with_offset (tree_model_sort, level, + gtk_tree_path_get_indices (s_path)[i], + NULL); + + g_return_if_fail (elt != NULL); + + if (!elt->children) + { + /* not covering this signal */ + goto done; + } + + level = elt->children; + parent_level = level; + i++; + } + + if (!parent_level) + goto done; + + if (level->ref_count == 0 && level != priv->root) + { + gtk_tree_model_sort_free_level (tree_model_sort, level, TRUE); + goto done; + } + + if (!gtk_tree_model_sort_insert_value (tree_model_sort, + parent_level, + s_path, + &real_s_iter)) + goto done; + + done_and_submit: + path = gtk_real_tree_model_sort_convert_child_path_to_path (tree_model_sort, + s_path, + FALSE); + + if (!path) + return; + + gtk_tree_model_sort_increment_stamp (tree_model_sort); + + gtk_tree_model_get_iter (GTK_TREE_MODEL (data), &iter, path); + gtk_tree_model_row_inserted (GTK_TREE_MODEL (data), path, &iter); + gtk_tree_path_free (path); + + done: + if (free_s_path) + gtk_tree_path_free (s_path); + + return; +} + +static void +gtk_tree_model_sort_row_has_child_toggled (GtkTreeModel *s_model, + GtkTreePath *s_path, + GtkTreeIter *s_iter, + gpointer data) +{ + GtkTreeModelSort *tree_model_sort = GTK_TREE_MODEL_SORT (data); + GtkTreePath *path; + GtkTreeIter iter; + + g_return_if_fail (s_path != NULL && s_iter != NULL); + + path = gtk_real_tree_model_sort_convert_child_path_to_path (tree_model_sort, s_path, FALSE); + if (path == NULL) + return; + + gtk_tree_model_get_iter (GTK_TREE_MODEL (data), &iter, path); + gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (data), path, &iter); + + gtk_tree_path_free (path); +} + +static void +gtk_tree_model_sort_row_deleted (GtkTreeModel *s_model, + GtkTreePath *s_path, + gpointer data) +{ + GtkTreeModelSort *tree_model_sort = GTK_TREE_MODEL_SORT (data); + GtkTreePath *path = NULL; + SortElt *elt; + SortLevel *level; + GtkTreeIter iter; + int offset; + + g_return_if_fail (s_path != NULL); + + path = gtk_real_tree_model_sort_convert_child_path_to_path (tree_model_sort, s_path, FALSE); + if (path == NULL) + return; + + gtk_tree_model_get_iter (GTK_TREE_MODEL (data), &iter, path); + + level = SORT_LEVEL (iter.user_data); + elt = SORT_ELT (iter.user_data2); + offset = elt->offset; + + gtk_tree_model_get_iter (GTK_TREE_MODEL (data), &iter, path); + + while (elt->ref_count > 0) + gtk_tree_model_sort_real_unref_node (GTK_TREE_MODEL (data), &iter, FALSE); + + /* If this node has children, we free the level (recursively) here + * and specify that unref may not be used, because parent and its + * children have been removed by now. + */ + if (elt->children) + gtk_tree_model_sort_free_level (tree_model_sort, + elt->children, FALSE); + + if (level->ref_count == 0 && g_sequence_get_length (level->seq) == 1) + { + gtk_tree_model_sort_increment_stamp (tree_model_sort); + gtk_tree_model_row_deleted (GTK_TREE_MODEL (data), path); + gtk_tree_path_free (path); + + if (level == tree_model_sort->priv->root) + { + gtk_tree_model_sort_free_level (tree_model_sort, + tree_model_sort->priv->root, + TRUE); + tree_model_sort->priv->root = NULL; + } + return; + } + + g_sequence_remove (elt->siter); + elt = NULL; + + /* The sequence is not ordered on offset, so we traverse the entire + * sequence. + */ + g_sequence_foreach (level->seq, decrease_offset_iter, + GINT_TO_POINTER (offset)); + + gtk_tree_model_sort_increment_stamp (tree_model_sort); + gtk_tree_model_row_deleted (GTK_TREE_MODEL (data), path); + + gtk_tree_path_free (path); +} + +static void +gtk_tree_model_sort_rows_reordered (GtkTreeModel *s_model, + GtkTreePath *s_path, + GtkTreeIter *s_iter, + int *new_order, + gpointer data) +{ + SortLevel *level; + GtkTreeIter iter; + GtkTreePath *path; + int *tmp_array; + int i, length; + GSequenceIter *siter, *end_siter; + GtkTreeModelSort *tree_model_sort = GTK_TREE_MODEL_SORT (data); + GtkTreeModelSortPrivate *priv = tree_model_sort->priv; + + g_return_if_fail (new_order != NULL); + + if (s_path == NULL || gtk_tree_path_get_depth (s_path) == 0) + { + if (priv->root == NULL) + return; + path = gtk_tree_path_new (); + level = SORT_LEVEL (priv->root); + } + else + { + SortElt *elt; + + path = gtk_real_tree_model_sort_convert_child_path_to_path (tree_model_sort, s_path, FALSE); + if (path == NULL) + return; + gtk_tree_model_get_iter (GTK_TREE_MODEL (data), &iter, path); + + elt = SORT_ELT (iter.user_data2); + + if (!elt->children) + { + gtk_tree_path_free (path); + return; + } + + level = elt->children; + } + + length = g_sequence_get_length (level->seq); + if (length < 2) + { + gtk_tree_path_free (path); + return; + } + + tmp_array = g_new (int, length); + + /* FIXME: I need to think about whether this can be done in a more + * efficient way? + */ + i = 0; + end_siter = g_sequence_get_end_iter (level->seq); + for (siter = g_sequence_get_begin_iter (level->seq); + siter != end_siter; + siter = g_sequence_iter_next (siter)) + { + int j; + SortElt *elt = g_sequence_get (siter); + + for (j = 0; j < length; j++) + { + if (elt->offset == new_order[j]) + tmp_array[i] = j; + } + + i++; + } + + /* This loop cannot be merged with the above loop nest, because that + * would introduce duplicate offsets. + */ + i = 0; + end_siter = g_sequence_get_end_iter (level->seq); + for (siter = g_sequence_get_begin_iter (level->seq); + siter != end_siter; + siter = g_sequence_iter_next (siter)) + { + SortElt *elt = g_sequence_get (siter); + + elt->offset = tmp_array[i]; + i++; + } + g_free (tmp_array); + + if (priv->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID && + priv->default_sort_func == NO_SORT_FUNC) + { + gtk_tree_model_sort_sort_level (tree_model_sort, level, + FALSE, FALSE); + gtk_tree_model_sort_increment_stamp (tree_model_sort); + + if (gtk_tree_path_get_depth (path)) + { + gtk_tree_model_get_iter (GTK_TREE_MODEL (tree_model_sort), + &iter, + path); + gtk_tree_model_rows_reordered (GTK_TREE_MODEL (tree_model_sort), + path, &iter, new_order); + } + else + { + gtk_tree_model_rows_reordered (GTK_TREE_MODEL (tree_model_sort), + path, NULL, new_order); + } + } + + gtk_tree_path_free (path); +} + +/* Fulfill our model requirements */ +static GtkTreeModelFlags +gtk_tree_model_sort_get_flags (GtkTreeModel *tree_model) +{ + GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model; + GtkTreeModelFlags flags; + + g_return_val_if_fail (tree_model_sort->priv->child_model != NULL, 0); + + flags = gtk_tree_model_get_flags (tree_model_sort->priv->child_model); + + if ((flags & GTK_TREE_MODEL_LIST_ONLY) == GTK_TREE_MODEL_LIST_ONLY) + return GTK_TREE_MODEL_LIST_ONLY; + + return 0; +} + +static int +gtk_tree_model_sort_get_n_columns (GtkTreeModel *tree_model) +{ + GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model; + + if (tree_model_sort->priv->child_model == 0) + return 0; + + return gtk_tree_model_get_n_columns (tree_model_sort->priv->child_model); +} + +static GType +gtk_tree_model_sort_get_column_type (GtkTreeModel *tree_model, + int index) +{ + GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model; + + g_return_val_if_fail (tree_model_sort->priv->child_model != NULL, G_TYPE_INVALID); + + return gtk_tree_model_get_column_type (tree_model_sort->priv->child_model, index); +} + +static gboolean +gtk_tree_model_sort_get_iter (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreePath *path) +{ + GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model; + GtkTreeModelSortPrivate *priv = tree_model_sort->priv; + int *indices; + SortElt *elt; + SortLevel *level; + int depth, i; + GSequenceIter *siter; + + g_return_val_if_fail (priv->child_model != NULL, FALSE); + + indices = gtk_tree_path_get_indices (path); + + if (priv->root == NULL) + gtk_tree_model_sort_build_level (tree_model_sort, NULL, NULL); + level = SORT_LEVEL (priv->root); + + depth = gtk_tree_path_get_depth (path); + if (depth == 0) + { + iter->stamp = 0; + return FALSE; + } + + for (i = 0; i < depth - 1; i++) + { + if ((level == NULL) || + (indices[i] >= g_sequence_get_length (level->seq))) + { + iter->stamp = 0; + return FALSE; + } + + siter = g_sequence_get_iter_at_pos (level->seq, indices[i]); + if (g_sequence_iter_is_end (siter)) + { + iter->stamp = 0; + return FALSE; + } + + elt = GET_ELT (siter); + g_assert (elt); + if (elt->children == NULL) + gtk_tree_model_sort_build_level (tree_model_sort, level, elt); + + level = elt->children; + } + + if (!level || indices[i] >= g_sequence_get_length (level->seq)) + { + iter->stamp = 0; + return FALSE; + } + + iter->stamp = priv->stamp; + iter->user_data = level; + + siter = g_sequence_get_iter_at_pos (level->seq, indices[depth - 1]); + if (g_sequence_iter_is_end (siter)) + { + iter->stamp = 0; + return FALSE; + } + iter->user_data2 = GET_ELT (siter); + + return TRUE; +} + +static GtkTreePath * +gtk_tree_model_sort_get_path (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model; + GtkTreeModelSortPrivate *priv = tree_model_sort->priv; + GtkTreePath *retval; + SortLevel *level; + SortElt *elt; + + g_return_val_if_fail (priv->child_model != NULL, NULL); + g_return_val_if_fail (priv->stamp == iter->stamp, NULL); + + retval = gtk_tree_path_new (); + + level = SORT_LEVEL (iter->user_data); + elt = SORT_ELT (iter->user_data2); + + while (level) + { + int index; + + index = g_sequence_iter_get_position (elt->siter); + gtk_tree_path_prepend_index (retval, index); + + elt = level->parent_elt; + level = level->parent_level; + } + + return retval; +} + +static void +gtk_tree_model_sort_get_value (GtkTreeModel *tree_model, + GtkTreeIter *iter, + int column, + GValue *value) +{ + GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model; + GtkTreeModelSortPrivate *priv = tree_model_sort->priv; + GtkTreeIter child_iter; + + g_return_if_fail (priv->child_model != NULL); + g_return_if_fail (VALID_ITER (iter, tree_model_sort)); + + GET_CHILD_ITER (tree_model_sort, &child_iter, iter); + gtk_tree_model_get_value (priv->child_model, + &child_iter, column, value); +} + +static gboolean +gtk_tree_model_sort_iter_next (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model; + GtkTreeModelSortPrivate *priv = tree_model_sort->priv; + SortElt *elt; + GSequenceIter *siter; + + g_return_val_if_fail (priv->child_model != NULL, FALSE); + g_return_val_if_fail (priv->stamp == iter->stamp, FALSE); + + elt = iter->user_data2; + + siter = g_sequence_iter_next (elt->siter); + if (g_sequence_iter_is_end (siter)) + { + iter->stamp = 0; + return FALSE; + } + iter->user_data2 = GET_ELT (siter); + + return TRUE; +} + +static gboolean +gtk_tree_model_sort_iter_previous (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model; + GtkTreeModelSortPrivate *priv = tree_model_sort->priv; + SortElt *elt; + GSequenceIter *siter; + + g_return_val_if_fail (priv->child_model != NULL, FALSE); + g_return_val_if_fail (priv->stamp == iter->stamp, FALSE); + + elt = iter->user_data2; + + if (g_sequence_iter_is_begin (elt->siter)) + { + iter->stamp = 0; + return FALSE; + } + + siter = g_sequence_iter_prev (elt->siter); + iter->user_data2 = GET_ELT (siter); + + return TRUE; +} + +static gboolean +gtk_tree_model_sort_iter_children (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent) +{ + GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model; + GtkTreeModelSortPrivate *priv = tree_model_sort->priv; + SortLevel *level; + + iter->stamp = 0; + g_return_val_if_fail (priv->child_model != NULL, FALSE); + if (parent) + g_return_val_if_fail (VALID_ITER (parent, tree_model_sort), FALSE); + + if (parent == NULL) + { + if (priv->root == NULL) + gtk_tree_model_sort_build_level (tree_model_sort, NULL, NULL); + if (priv->root == NULL) + return FALSE; + + level = priv->root; + iter->stamp = priv->stamp; + iter->user_data = level; + iter->user_data2 = g_sequence_get (g_sequence_get_begin_iter (level->seq)); + } + else + { + SortElt *elt; + + level = SORT_LEVEL (parent->user_data); + elt = SORT_ELT (parent->user_data2); + + if (elt->children == NULL) + gtk_tree_model_sort_build_level (tree_model_sort, level, elt); + + if (elt->children == NULL) + return FALSE; + + iter->stamp = priv->stamp; + iter->user_data = elt->children; + iter->user_data2 = g_sequence_get (g_sequence_get_begin_iter (elt->children->seq)); + } + + return TRUE; +} + +static gboolean +gtk_tree_model_sort_iter_has_child (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model; + GtkTreeModelSortPrivate *priv = tree_model_sort->priv; + GtkTreeIter child_iter; + + g_return_val_if_fail (priv->child_model != NULL, FALSE); + g_return_val_if_fail (VALID_ITER (iter, tree_model_sort), FALSE); + + GET_CHILD_ITER (tree_model_sort, &child_iter, iter); + + return gtk_tree_model_iter_has_child (priv->child_model, &child_iter); +} + +static int +gtk_tree_model_sort_iter_n_children (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model; + GtkTreeModelSortPrivate *priv = tree_model_sort->priv; + GtkTreeIter child_iter; + + g_return_val_if_fail (priv->child_model != NULL, 0); + if (iter) + g_return_val_if_fail (VALID_ITER (iter, tree_model_sort), 0); + + if (iter == NULL) + return gtk_tree_model_iter_n_children (priv->child_model, NULL); + + GET_CHILD_ITER (tree_model_sort, &child_iter, iter); + + return gtk_tree_model_iter_n_children (priv->child_model, &child_iter); +} + +static gboolean +gtk_tree_model_sort_iter_nth_child (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent, + int n) +{ + GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model; + SortLevel *level; + /* We have this for the iter == parent case */ + GtkTreeIter children; + + if (parent) + g_return_val_if_fail (VALID_ITER (parent, tree_model_sort), FALSE); + + /* Use this instead of has_child to force us to build the level, if needed */ + if (gtk_tree_model_sort_iter_children (tree_model, &children, parent) == FALSE) + { + iter->stamp = 0; + return FALSE; + } + + level = children.user_data; + if (n >= g_sequence_get_length (level->seq)) + { + iter->stamp = 0; + return FALSE; + } + + iter->stamp = tree_model_sort->priv->stamp; + iter->user_data = level; + iter->user_data2 = g_sequence_get (g_sequence_get_iter_at_pos (level->seq, n)); + + return TRUE; +} + +static gboolean +gtk_tree_model_sort_iter_parent (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *child) +{ + GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model; + GtkTreeModelSortPrivate *priv = tree_model_sort->priv; + SortLevel *level; + + iter->stamp = 0; + g_return_val_if_fail (priv->child_model != NULL, FALSE); + g_return_val_if_fail (VALID_ITER (child, tree_model_sort), FALSE); + + level = child->user_data; + + if (level->parent_level) + { + iter->stamp = priv->stamp; + iter->user_data = level->parent_level; + iter->user_data2 = level->parent_elt; + + return TRUE; + } + return FALSE; +} + +static void +gtk_tree_model_sort_ref_node (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model; + GtkTreeModelSortPrivate *priv = tree_model_sort->priv; + GtkTreeIter child_iter; + SortLevel *level; + SortElt *elt; + + g_return_if_fail (priv->child_model != NULL); + g_return_if_fail (VALID_ITER (iter, tree_model_sort)); + + GET_CHILD_ITER (tree_model_sort, &child_iter, iter); + + /* Reference the node in the child model */ + gtk_tree_model_ref_node (priv->child_model, &child_iter); + + /* Increase the reference count of this element and its level */ + level = iter->user_data; + elt = iter->user_data2; + + elt->ref_count++; + level->ref_count++; + + if (level->ref_count == 1) + { + SortLevel *parent_level = level->parent_level; + SortElt *parent_elt = level->parent_elt; + + /* We were at zero -- time to decrement the zero_ref_count val */ + while (parent_level) + { + parent_elt->zero_ref_count--; + + parent_elt = parent_level->parent_elt; + parent_level = parent_level->parent_level; + } + + if (priv->root != level) + priv->zero_ref_count--; + } +} + +static void +gtk_tree_model_sort_real_unref_node (GtkTreeModel *tree_model, + GtkTreeIter *iter, + gboolean propagate_unref) +{ + GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model; + GtkTreeModelSortPrivate *priv = tree_model_sort->priv; + SortLevel *level; + SortElt *elt; + + g_return_if_fail (priv->child_model != NULL); + g_return_if_fail (VALID_ITER (iter, tree_model_sort)); + + if (propagate_unref) + { + GtkTreeIter child_iter; + + GET_CHILD_ITER (tree_model_sort, &child_iter, iter); + gtk_tree_model_unref_node (priv->child_model, &child_iter); + } + + level = iter->user_data; + elt = iter->user_data2; + + g_return_if_fail (elt->ref_count > 0); + + elt->ref_count--; + level->ref_count--; + + if (level->ref_count == 0) + { + SortLevel *parent_level = level->parent_level; + SortElt *parent_elt = level->parent_elt; + + /* We are at zero -- time to increment the zero_ref_count val */ + while (parent_level) + { + parent_elt->zero_ref_count++; + + parent_elt = parent_level->parent_elt; + parent_level = parent_level->parent_level; + } + + if (priv->root != level) + priv->zero_ref_count++; + } +} + +static void +gtk_tree_model_sort_unref_node (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + gtk_tree_model_sort_real_unref_node (tree_model, iter, TRUE); +} + +/* Sortable interface */ +static gboolean +gtk_tree_model_sort_get_sort_column_id (GtkTreeSortable *sortable, + int *sort_column_id, + GtkSortType *order) +{ + GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *)sortable; + GtkTreeModelSortPrivate *priv = tree_model_sort->priv; + + if (sort_column_id) + *sort_column_id = priv->sort_column_id; + if (order) + *order = priv->order; + + if (priv->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID || + priv->sort_column_id == GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID) + return FALSE; + + return TRUE; +} + +static void +gtk_tree_model_sort_set_sort_column_id (GtkTreeSortable *sortable, + int sort_column_id, + GtkSortType order) +{ + GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *)sortable; + GtkTreeModelSortPrivate *priv = tree_model_sort->priv; + + if (priv->sort_column_id == sort_column_id && priv->order == order) + return; + + if (sort_column_id != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID) + { + if (sort_column_id != GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID) + { + GtkTreeDataSortHeader *header = NULL; + + header = _gtk_tree_data_list_get_header (priv->sort_list, + sort_column_id); + + /* we want to make sure that we have a function */ + g_return_if_fail (header != NULL); + g_return_if_fail (header->func != NULL); + } + else + g_return_if_fail (priv->default_sort_func != NULL); + } + + priv->sort_column_id = sort_column_id; + priv->order = order; + + gtk_tree_sortable_sort_column_changed (sortable); + + gtk_tree_model_sort_sort (tree_model_sort); +} + +static void +gtk_tree_model_sort_set_sort_func (GtkTreeSortable *sortable, + int sort_column_id, + GtkTreeIterCompareFunc func, + gpointer data, + GDestroyNotify destroy) +{ + GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) sortable; + GtkTreeModelSortPrivate *priv = tree_model_sort->priv; + + priv->sort_list = _gtk_tree_data_list_set_header (priv->sort_list, + sort_column_id, + func, data, destroy); + + if (priv->sort_column_id == sort_column_id) + gtk_tree_model_sort_sort (tree_model_sort); +} + +static void +gtk_tree_model_sort_set_default_sort_func (GtkTreeSortable *sortable, + GtkTreeIterCompareFunc func, + gpointer data, + GDestroyNotify destroy) +{ + GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *)sortable; + GtkTreeModelSortPrivate *priv = tree_model_sort->priv; + + if (priv->default_sort_destroy) + { + GDestroyNotify d = priv->default_sort_destroy; + + priv->default_sort_destroy = NULL; + d (priv->default_sort_data); + } + + priv->default_sort_func = func; + priv->default_sort_data = data; + priv->default_sort_destroy = destroy; + + if (priv->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID) + gtk_tree_model_sort_sort (tree_model_sort); +} + +static gboolean +gtk_tree_model_sort_has_default_sort_func (GtkTreeSortable *sortable) +{ + GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *)sortable; + + return (tree_model_sort->priv->default_sort_func != NO_SORT_FUNC); +} + +/* DragSource interface */ +static gboolean +gtk_tree_model_sort_row_draggable (GtkTreeDragSource *drag_source, + GtkTreePath *path) +{ + GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *)drag_source; + GtkTreePath *child_path; + gboolean draggable; + + child_path = gtk_tree_model_sort_convert_path_to_child_path (tree_model_sort, + path); + draggable = gtk_tree_drag_source_row_draggable (GTK_TREE_DRAG_SOURCE (tree_model_sort->priv->child_model), child_path); + gtk_tree_path_free (child_path); + + return draggable; +} + +static GdkContentProvider * +gtk_tree_model_sort_drag_data_get (GtkTreeDragSource *drag_source, + GtkTreePath *path) +{ + GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *)drag_source; + GtkTreePath *child_path; + GdkContentProvider *gotten; + + child_path = gtk_tree_model_sort_convert_path_to_child_path (tree_model_sort, path); + gotten = gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (tree_model_sort->priv->child_model), child_path); + gtk_tree_path_free (child_path); + + return gotten; +} + +static gboolean +gtk_tree_model_sort_drag_data_delete (GtkTreeDragSource *drag_source, + GtkTreePath *path) +{ + GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *)drag_source; + GtkTreePath *child_path; + gboolean deleted; + + child_path = gtk_tree_model_sort_convert_path_to_child_path (tree_model_sort, path); + deleted = gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (tree_model_sort->priv->child_model), child_path); + gtk_tree_path_free (child_path); + + return deleted; +} + +/* sorting code - private */ +static int +gtk_tree_model_sort_compare_func (gconstpointer a, + gconstpointer b, + gpointer user_data) +{ + SortData *data = (SortData *)user_data; + GtkTreeModelSort *tree_model_sort = data->tree_model_sort; + GtkTreeModelSortPrivate *priv = tree_model_sort->priv; + const SortElt *sa = a; + const SortElt *sb = b; + + GtkTreeIter iter_a, iter_b; + int retval; + + if (GTK_TREE_MODEL_SORT_CACHE_CHILD_ITERS (tree_model_sort)) + { + iter_a = sa->iter; + iter_b = sb->iter; + } + else + { + data->parent_path_indices [data->parent_path_depth-1] = sa->offset; + gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->child_model), &iter_a, data->parent_path); + data->parent_path_indices [data->parent_path_depth-1] = sb->offset; + gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->child_model), &iter_b, data->parent_path); + } + + retval = (* data->sort_func) (GTK_TREE_MODEL (priv->child_model), + &iter_a, &iter_b, + data->sort_data); + + if (priv->order == GTK_SORT_DESCENDING) + { + if (retval > 0) + retval = -1; + else if (retval < 0) + retval = 1; + } + + return retval; +} + +static int +gtk_tree_model_sort_offset_compare_func (gconstpointer a, + gconstpointer b, + gpointer user_data) +{ + int retval; + + const SortElt *sa = (SortElt *)a; + const SortElt *sb = (SortElt *)b; + + SortData *data = (SortData *)user_data; + + if (sa->offset < sb->offset) + retval = -1; + else if (sa->offset > sb->offset) + retval = 1; + else + retval = 0; + + if (data->tree_model_sort->priv->order == GTK_SORT_DESCENDING) + { + if (retval > 0) + retval = -1; + else if (retval < 0) + retval = 1; + } + + return retval; +} + +static void +gtk_tree_model_sort_sort_level (GtkTreeModelSort *tree_model_sort, + SortLevel *level, + gboolean recurse, + gboolean emit_reordered) +{ + GtkTreeModelSortPrivate *priv = tree_model_sort->priv; + int i; + GSequenceIter *begin_siter, *end_siter, *siter; + SortElt *begin_elt; + int *new_order; + + GtkTreeIter iter; + GtkTreePath *path; + + SortData data; + + g_return_if_fail (level != NULL); + + begin_siter = g_sequence_get_begin_iter (level->seq); + begin_elt = g_sequence_get (begin_siter); + + if (g_sequence_get_length (level->seq) < 1 && !begin_elt->children) + return; + + iter.stamp = priv->stamp; + iter.user_data = level; + iter.user_data2 = begin_elt; + + gtk_tree_model_sort_ref_node (GTK_TREE_MODEL (tree_model_sort), &iter); + + i = 0; + end_siter = g_sequence_get_end_iter (level->seq); + for (siter = g_sequence_get_begin_iter (level->seq); + siter != end_siter; + siter = g_sequence_iter_next (siter)) + { + SortElt *elt = g_sequence_get (siter); + + elt->old_index = i; + i++; + } + + fill_sort_data (&data, tree_model_sort, level); + + if (data.sort_func == NO_SORT_FUNC) + g_sequence_sort (level->seq, gtk_tree_model_sort_offset_compare_func, + &data); + else + g_sequence_sort (level->seq, gtk_tree_model_sort_compare_func, &data); + + free_sort_data (&data); + + new_order = g_new (int, g_sequence_get_length (level->seq)); + + i = 0; + end_siter = g_sequence_get_end_iter (level->seq); + for (siter = g_sequence_get_begin_iter (level->seq); + siter != end_siter; + siter = g_sequence_iter_next (siter)) + { + SortElt *elt = g_sequence_get (siter); + + new_order[i++] = elt->old_index; + } + + if (emit_reordered) + { + gtk_tree_model_sort_increment_stamp (tree_model_sort); + if (level->parent_elt) + { + iter.stamp = priv->stamp; + iter.user_data = level->parent_level; + iter.user_data2 = level->parent_elt; + + path = gtk_tree_model_get_path (GTK_TREE_MODEL (tree_model_sort), + &iter); + + gtk_tree_model_rows_reordered (GTK_TREE_MODEL (tree_model_sort), path, + &iter, new_order); + } + else + { + /* toplevel list */ + path = gtk_tree_path_new (); + gtk_tree_model_rows_reordered (GTK_TREE_MODEL (tree_model_sort), path, + NULL, new_order); + } + + gtk_tree_path_free (path); + } + + /* recurse, if possible */ + if (recurse) + { + end_siter = g_sequence_get_end_iter (level->seq); + for (siter = g_sequence_get_begin_iter (level->seq); + siter != end_siter; + siter = g_sequence_iter_next (siter)) + { + SortElt *elt = g_sequence_get (siter); + + if (elt->children) + gtk_tree_model_sort_sort_level (tree_model_sort, + elt->children, + TRUE, emit_reordered); + } + } + + g_free (new_order); + + /* get the iter we referenced at the beginning of this function and + * unref it again + */ + iter.stamp = priv->stamp; + iter.user_data = level; + iter.user_data2 = begin_elt; + + gtk_tree_model_sort_unref_node (GTK_TREE_MODEL (tree_model_sort), &iter); +} + +static void +gtk_tree_model_sort_sort (GtkTreeModelSort *tree_model_sort) +{ + GtkTreeModelSortPrivate *priv = tree_model_sort->priv; + + if (priv->sort_column_id == GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID) + return; + + if (!priv->root) + return; + + if (priv->sort_column_id != GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID) + { + GtkTreeDataSortHeader *header = NULL; + + header = _gtk_tree_data_list_get_header (priv->sort_list, + priv->sort_column_id); + + /* we want to make sure that we have a function */ + g_return_if_fail (header != NULL); + g_return_if_fail (header->func != NULL); + } + else + g_return_if_fail (priv->default_sort_func != NULL); + + gtk_tree_model_sort_sort_level (tree_model_sort, priv->root, + TRUE, TRUE); +} + +/* signal helpers */ +static gboolean +gtk_tree_model_sort_insert_value (GtkTreeModelSort *tree_model_sort, + SortLevel *level, + GtkTreePath *s_path, + GtkTreeIter *s_iter) +{ + GtkTreeModelSortPrivate *priv = tree_model_sort->priv; + SortElt *elt; + SortData data; + int offset; + + elt = sort_elt_new (); + + offset = gtk_tree_path_get_indices (s_path)[gtk_tree_path_get_depth (s_path) - 1]; + + if (GTK_TREE_MODEL_SORT_CACHE_CHILD_ITERS (tree_model_sort)) + elt->iter = *s_iter; + elt->offset = offset; + elt->zero_ref_count = 0; + elt->ref_count = 0; + elt->children = NULL; + + /* update all larger offsets */ + g_sequence_foreach (level->seq, increase_offset_iter, GINT_TO_POINTER (offset)); + + fill_sort_data (&data, tree_model_sort, level); + + if (priv->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID && + priv->default_sort_func == NO_SORT_FUNC) + { + elt->siter = g_sequence_insert_sorted (level->seq, elt, + gtk_tree_model_sort_offset_compare_func, + &data); + } + else + { + elt->siter = g_sequence_insert_sorted (level->seq, elt, + gtk_tree_model_sort_compare_func, + &data); + } + + free_sort_data (&data); + + return TRUE; +} + +/* sort elt stuff */ +static GtkTreePath * +gtk_tree_model_sort_elt_get_path (SortLevel *level, + SortElt *elt) +{ + SortLevel *walker = level; + SortElt *walker2 = elt; + GtkTreePath *path; + + g_return_val_if_fail (level != NULL, NULL); + g_return_val_if_fail (elt != NULL, NULL); + + path = gtk_tree_path_new (); + + while (walker) + { + gtk_tree_path_prepend_index (path, walker2->offset); + + if (!walker->parent_level) + break; + + walker2 = walker->parent_elt; + walker = walker->parent_level; + } + + return path; +} + +/** + * gtk_tree_model_sort_set_model: + * @tree_model_sort: The `GtkTreeModelSort`. + * @child_model: (nullable): A `GtkTreeModel` + * + * Sets the model of @tree_model_sort to be @model. If @model is %NULL, + * then the old model is unset. The sort function is unset as a result + * of this call. The model will be in an unsorted state until a sort + * function is set. + * + * Deprecated: 4.10 + **/ +static void +gtk_tree_model_sort_set_model (GtkTreeModelSort *tree_model_sort, + GtkTreeModel *child_model) +{ + GtkTreeModelSortPrivate *priv = tree_model_sort->priv; + + if (child_model) + g_object_ref (child_model); + + if (priv->child_model) + { + g_signal_handler_disconnect (priv->child_model, + priv->changed_id); + g_signal_handler_disconnect (priv->child_model, + priv->inserted_id); + g_signal_handler_disconnect (priv->child_model, + priv->has_child_toggled_id); + g_signal_handler_disconnect (priv->child_model, + priv->deleted_id); + g_signal_handler_disconnect (priv->child_model, + priv->reordered_id); + + /* reset our state */ + if (priv->root) + gtk_tree_model_sort_free_level (tree_model_sort, priv->root, TRUE); + priv->root = NULL; + _gtk_tree_data_list_header_free (priv->sort_list); + priv->sort_list = NULL; + g_object_unref (priv->child_model); + } + + priv->child_model = child_model; + + if (child_model) + { + GType *types; + int i, n_columns; + + priv->changed_id = + g_signal_connect (child_model, "row-changed", + G_CALLBACK (gtk_tree_model_sort_row_changed), + tree_model_sort); + priv->inserted_id = + g_signal_connect (child_model, "row-inserted", + G_CALLBACK (gtk_tree_model_sort_row_inserted), + tree_model_sort); + priv->has_child_toggled_id = + g_signal_connect (child_model, "row-has-child-toggled", + G_CALLBACK (gtk_tree_model_sort_row_has_child_toggled), + tree_model_sort); + priv->deleted_id = + g_signal_connect (child_model, "row-deleted", + G_CALLBACK (gtk_tree_model_sort_row_deleted), + tree_model_sort); + priv->reordered_id = + g_signal_connect (child_model, "rows-reordered", + G_CALLBACK (gtk_tree_model_sort_rows_reordered), + tree_model_sort); + + priv->child_flags = gtk_tree_model_get_flags (child_model); + n_columns = gtk_tree_model_get_n_columns (child_model); + + types = g_new (GType, n_columns); + for (i = 0; i < n_columns; i++) + types[i] = gtk_tree_model_get_column_type (child_model, i); + + priv->sort_list = _gtk_tree_data_list_header_new (n_columns, types); + g_free (types); + + priv->default_sort_func = NO_SORT_FUNC; + priv->stamp = g_random_int (); + } +} + +/** + * gtk_tree_model_sort_get_model: + * @tree_model: a `GtkTreeModelSort` + * + * Returns the model the `GtkTreeModelSort` is sorting. + * + * Returns: (transfer none): the "child model" being sorted + **/ +GtkTreeModel * +gtk_tree_model_sort_get_model (GtkTreeModelSort *tree_model) +{ + g_return_val_if_fail (GTK_IS_TREE_MODEL_SORT (tree_model), NULL); + + return tree_model->priv->child_model; +} + + +static GtkTreePath * +gtk_real_tree_model_sort_convert_child_path_to_path (GtkTreeModelSort *tree_model_sort, + GtkTreePath *child_path, + gboolean build_levels) +{ + GtkTreeModelSortPrivate *priv = tree_model_sort->priv; + int *child_indices; + GtkTreePath *retval; + SortLevel *level; + int i; + + g_return_val_if_fail (priv->child_model != NULL, NULL); + g_return_val_if_fail (child_path != NULL, NULL); + + retval = gtk_tree_path_new (); + child_indices = gtk_tree_path_get_indices (child_path); + + if (priv->root == NULL && build_levels) + gtk_tree_model_sort_build_level (tree_model_sort, NULL, NULL); + level = SORT_LEVEL (priv->root); + + for (i = 0; i < gtk_tree_path_get_depth (child_path); i++) + { + SortElt *tmp; + GSequenceIter *siter; + gboolean found_child = FALSE; + + if (!level) + { + gtk_tree_path_free (retval); + return NULL; + } + + if (child_indices[i] >= g_sequence_get_length (level->seq)) + { + gtk_tree_path_free (retval); + return NULL; + } + tmp = lookup_elt_with_offset (tree_model_sort, level, + child_indices[i], &siter); + if (tmp) + { + gtk_tree_path_append_index (retval, g_sequence_iter_get_position (siter)); + if (tmp->children == NULL && build_levels) + gtk_tree_model_sort_build_level (tree_model_sort, level, tmp); + + level = tmp->children; + found_child = TRUE; + } + + if (! found_child) + { + gtk_tree_path_free (retval); + return NULL; + } + } + + return retval; +} + + +/** + * gtk_tree_model_sort_convert_child_path_to_path: + * @tree_model_sort: A `GtkTreeModelSort` + * @child_path: A `GtkTreePath` to convert + * + * Converts @child_path to a path relative to @tree_model_sort. That is, + * @child_path points to a path in the child model. The returned path will + * point to the same row in the sorted model. If @child_path isn’t a valid + * path on the child model, then %NULL is returned. + * + * Returns: (nullable) (transfer full): A newly allocated `GtkTreePath` + * + * Deprecated: 4.10 + **/ +GtkTreePath * +gtk_tree_model_sort_convert_child_path_to_path (GtkTreeModelSort *tree_model_sort, + GtkTreePath *child_path) +{ + g_return_val_if_fail (GTK_IS_TREE_MODEL_SORT (tree_model_sort), NULL); + g_return_val_if_fail (tree_model_sort->priv->child_model != NULL, NULL); + g_return_val_if_fail (child_path != NULL, NULL); + + return gtk_real_tree_model_sort_convert_child_path_to_path (tree_model_sort, child_path, TRUE); +} + +/** + * gtk_tree_model_sort_convert_child_iter_to_iter: + * @tree_model_sort: A `GtkTreeModelSort` + * @sort_iter: (out): An uninitialized `GtkTreeIter` + * @child_iter: A valid `GtkTreeIter` pointing to a row on the child model + * + * Sets @sort_iter to point to the row in @tree_model_sort that corresponds to + * the row pointed at by @child_iter. If @sort_iter was not set, %FALSE + * is returned. Note: a boolean is only returned since 2.14. + * + * Returns: %TRUE, if @sort_iter was set, i.e. if @sort_iter is a + * valid iterator pointer to a visible row in the child model. + * + * Deprecated: 4.10 + **/ +gboolean +gtk_tree_model_sort_convert_child_iter_to_iter (GtkTreeModelSort *tree_model_sort, + GtkTreeIter *sort_iter, + GtkTreeIter *child_iter) +{ + gboolean ret; + GtkTreePath *child_path, *path; + GtkTreeModelSortPrivate *priv = tree_model_sort->priv; + + g_return_val_if_fail (GTK_IS_TREE_MODEL_SORT (tree_model_sort), FALSE); + g_return_val_if_fail (priv->child_model != NULL, FALSE); + g_return_val_if_fail (sort_iter != NULL, FALSE); + g_return_val_if_fail (child_iter != NULL, FALSE); + g_return_val_if_fail (sort_iter != child_iter, FALSE); + + sort_iter->stamp = 0; + + child_path = gtk_tree_model_get_path (priv->child_model, child_iter); + g_return_val_if_fail (child_path != NULL, FALSE); + + path = gtk_tree_model_sort_convert_child_path_to_path (tree_model_sort, child_path); + gtk_tree_path_free (child_path); + + if (!path) + { + g_warning ("%s: The conversion of the child path to a GtkTreeModel sort path failed", G_STRLOC); + return FALSE; + } + + ret = gtk_tree_model_get_iter (GTK_TREE_MODEL (tree_model_sort), + sort_iter, path); + gtk_tree_path_free (path); + + return ret; +} + +/** + * gtk_tree_model_sort_convert_path_to_child_path: + * @tree_model_sort: A `GtkTreeModelSort` + * @sorted_path: A `GtkTreePath` to convert + * + * Converts @sorted_path to a path on the child model of @tree_model_sort. + * That is, @sorted_path points to a location in @tree_model_sort. The + * returned path will point to the same location in the model not being + * sorted. If @sorted_path does not point to a location in the child model, + * %NULL is returned. + * + * Returns: (nullable) (transfer full): A newly allocated `GtkTreePath` + * + * Deprecated: 4.10 + **/ +GtkTreePath * +gtk_tree_model_sort_convert_path_to_child_path (GtkTreeModelSort *tree_model_sort, + GtkTreePath *sorted_path) +{ + GtkTreeModelSortPrivate *priv = tree_model_sort->priv; + int *sorted_indices; + GtkTreePath *retval; + SortLevel *level; + int i; + + g_return_val_if_fail (GTK_IS_TREE_MODEL_SORT (tree_model_sort), NULL); + g_return_val_if_fail (priv->child_model != NULL, NULL); + g_return_val_if_fail (sorted_path != NULL, NULL); + + retval = gtk_tree_path_new (); + sorted_indices = gtk_tree_path_get_indices (sorted_path); + if (priv->root == NULL) + gtk_tree_model_sort_build_level (tree_model_sort, NULL, NULL); + level = SORT_LEVEL (priv->root); + + for (i = 0; i < gtk_tree_path_get_depth (sorted_path); i++) + { + SortElt *elt = NULL; + GSequenceIter *siter; + + if ((level == NULL) || + (g_sequence_get_length (level->seq) <= sorted_indices[i])) + { + gtk_tree_path_free (retval); + return NULL; + } + + siter = g_sequence_get_iter_at_pos (level->seq, sorted_indices[i]); + if (g_sequence_iter_is_end (siter)) + { + gtk_tree_path_free (retval); + return NULL; + } + + elt = GET_ELT (siter); + g_assert (elt); + if (elt->children == NULL) + gtk_tree_model_sort_build_level (tree_model_sort, level, elt); + + if (level == NULL) + { + gtk_tree_path_free (retval); + break; + } + + gtk_tree_path_append_index (retval, elt->offset); + level = elt->children; + } + + return retval; +} + +/** + * gtk_tree_model_sort_convert_iter_to_child_iter: + * @tree_model_sort: A `GtkTreeModelSort` + * @child_iter: (out): An uninitialized `GtkTreeIter` + * @sorted_iter: A valid `GtkTreeIter` pointing to a row on @tree_model_sort. + * + * Sets @child_iter to point to the row pointed to by @sorted_iter. + * + * Deprecated: 4.10 + **/ +void +gtk_tree_model_sort_convert_iter_to_child_iter (GtkTreeModelSort *tree_model_sort, + GtkTreeIter *child_iter, + GtkTreeIter *sorted_iter) +{ + GtkTreeModelSortPrivate *priv = tree_model_sort->priv; + g_return_if_fail (GTK_IS_TREE_MODEL_SORT (tree_model_sort)); + g_return_if_fail (priv->child_model != NULL); + g_return_if_fail (child_iter != NULL); + g_return_if_fail (VALID_ITER (sorted_iter, tree_model_sort)); + g_return_if_fail (sorted_iter != child_iter); + + if (GTK_TREE_MODEL_SORT_CACHE_CHILD_ITERS (tree_model_sort)) + { + *child_iter = SORT_ELT (sorted_iter->user_data2)->iter; + } + else + { + GtkTreePath *path; + gboolean valid = FALSE; + + path = gtk_tree_model_sort_elt_get_path (sorted_iter->user_data, + sorted_iter->user_data2); + valid = gtk_tree_model_get_iter (priv->child_model, child_iter, path); + gtk_tree_path_free (path); + + g_return_if_fail (valid == TRUE); + } +} + +static void +gtk_tree_model_sort_build_level (GtkTreeModelSort *tree_model_sort, + SortLevel *parent_level, + SortElt *parent_elt) +{ + GtkTreeModelSortPrivate *priv = tree_model_sort->priv; + GtkTreeIter iter; + SortLevel *new_level; + int length = 0; + int i; + + g_assert (priv->child_model != NULL); + + if (parent_level == NULL) + { + if (gtk_tree_model_get_iter_first (priv->child_model, &iter) == FALSE) + return; + length = gtk_tree_model_iter_n_children (priv->child_model, NULL); + } + else + { + GtkTreeIter parent_iter; + GtkTreeIter child_parent_iter; + + parent_iter.stamp = priv->stamp; + parent_iter.user_data = parent_level; + parent_iter.user_data2 = parent_elt; + + gtk_tree_model_sort_convert_iter_to_child_iter (tree_model_sort, + &child_parent_iter, + &parent_iter); + if (gtk_tree_model_iter_children (priv->child_model, + &iter, + &child_parent_iter) == FALSE) + return; + + /* stamp may have changed */ + gtk_tree_model_sort_convert_iter_to_child_iter (tree_model_sort, + &child_parent_iter, + &parent_iter); + + length = gtk_tree_model_iter_n_children (priv->child_model, &child_parent_iter); + + gtk_tree_model_sort_ref_node (GTK_TREE_MODEL (tree_model_sort), + &parent_iter); + } + + g_return_if_fail (length > 0); + + new_level = g_new (SortLevel, 1); + new_level->seq = g_sequence_new (sort_elt_free); + new_level->ref_count = 0; + new_level->parent_level = parent_level; + new_level->parent_elt = parent_elt; + + if (parent_elt) + parent_elt->children = new_level; + else + priv->root = new_level; + + /* increase the count of zero ref_counts.*/ + while (parent_level) + { + parent_elt->zero_ref_count++; + + parent_elt = parent_level->parent_elt; + parent_level = parent_level->parent_level; + } + + if (new_level != priv->root) + priv->zero_ref_count++; + + for (i = 0; i < length; i++) + { + SortElt *sort_elt; + + sort_elt = sort_elt_new (); + sort_elt->offset = i; + sort_elt->zero_ref_count = 0; + sort_elt->ref_count = 0; + sort_elt->children = NULL; + + if (GTK_TREE_MODEL_SORT_CACHE_CHILD_ITERS (tree_model_sort)) + { + sort_elt->iter = iter; + if (gtk_tree_model_iter_next (priv->child_model, &iter) == FALSE && + i < length - 1) + { + if (parent_level) + { + GtkTreePath *level; + char *str; + + level = gtk_tree_model_sort_elt_get_path (parent_level, + parent_elt); + str = gtk_tree_path_to_string (level); + gtk_tree_path_free (level); + + g_warning ("%s: There is a discrepancy between the sort model " + "and the child model. The child model is " + "advertising a wrong length for level %s:.", + G_STRLOC, str); + g_free (str); + } + else + { + g_warning ("%s: There is a discrepancy between the sort model " + "and the child model. The child model is " + "advertising a wrong length for the root level.", + G_STRLOC); + } + + return; + } + } + + sort_elt->siter = g_sequence_append (new_level->seq, sort_elt); + } + + /* sort level */ + gtk_tree_model_sort_sort_level (tree_model_sort, new_level, + FALSE, FALSE); +} + +static void +gtk_tree_model_sort_free_level (GtkTreeModelSort *tree_model_sort, + SortLevel *sort_level, + gboolean unref) +{ + GtkTreeModelSortPrivate *priv = tree_model_sort->priv; + GSequenceIter *siter; + GSequenceIter *end_siter; + + g_assert (sort_level); + + end_siter = g_sequence_get_end_iter (sort_level->seq); + for (siter = g_sequence_get_begin_iter (sort_level->seq); + siter != end_siter; + siter = g_sequence_iter_next (siter)) + { + SortElt *elt = g_sequence_get (siter); + + if (elt->children) + gtk_tree_model_sort_free_level (tree_model_sort, + elt->children, unref); + } + + if (sort_level->ref_count == 0) + { + SortLevel *parent_level = sort_level->parent_level; + SortElt *parent_elt = sort_level->parent_elt; + + while (parent_level) + { + parent_elt->zero_ref_count--; + + parent_elt = parent_level->parent_elt; + parent_level = parent_level->parent_level; + } + + if (sort_level != priv->root) + priv->zero_ref_count--; + } + + if (sort_level->parent_elt) + { + if (unref) + { + GtkTreeIter parent_iter; + + parent_iter.stamp = tree_model_sort->priv->stamp; + parent_iter.user_data = sort_level->parent_level; + parent_iter.user_data2 = sort_level->parent_elt; + + gtk_tree_model_sort_unref_node (GTK_TREE_MODEL (tree_model_sort), + &parent_iter); + } + + sort_level->parent_elt->children = NULL; + } + else + priv->root = NULL; + + g_sequence_free (sort_level->seq); + sort_level->seq = NULL; + + g_free (sort_level); + sort_level = NULL; +} + +static void +gtk_tree_model_sort_increment_stamp (GtkTreeModelSort *tree_model_sort) +{ + GtkTreeModelSortPrivate *priv = tree_model_sort->priv; + + do + { + priv->stamp++; + } + while (priv->stamp == 0); + + gtk_tree_model_sort_clear_cache (tree_model_sort); +} + +static void +gtk_tree_model_sort_clear_cache_helper_iter (gpointer data, + gpointer user_data) +{ + GtkTreeModelSort *tree_model_sort = user_data; + SortElt *elt = data; + + if (elt->zero_ref_count > 0) + gtk_tree_model_sort_clear_cache_helper (tree_model_sort, elt->children); +} + +static void +gtk_tree_model_sort_clear_cache_helper (GtkTreeModelSort *tree_model_sort, + SortLevel *level) +{ + g_assert (level != NULL); + + g_sequence_foreach (level->seq, gtk_tree_model_sort_clear_cache_helper_iter, + tree_model_sort); + + if (level->ref_count == 0 && level != tree_model_sort->priv->root) + gtk_tree_model_sort_free_level (tree_model_sort, level, TRUE); +} + +/** + * gtk_tree_model_sort_reset_default_sort_func: + * @tree_model_sort: A `GtkTreeModelSort` + * + * This resets the default sort function to be in the “unsorted” state. That + * is, it is in the same order as the child model. It will re-sort the model + * to be in the same order as the child model only if the `GtkTreeModelSort` + * is in “unsorted” state. + * + * Deprecated: 4.10 + **/ +void +gtk_tree_model_sort_reset_default_sort_func (GtkTreeModelSort *tree_model_sort) +{ + GtkTreeModelSortPrivate *priv = tree_model_sort->priv; + + g_return_if_fail (GTK_IS_TREE_MODEL_SORT (tree_model_sort)); + + if (priv->default_sort_destroy) + { + GDestroyNotify d = priv->default_sort_destroy; + + priv->default_sort_destroy = NULL; + d (priv->default_sort_data); + } + + priv->default_sort_func = NO_SORT_FUNC; + priv->default_sort_data = NULL; + priv->default_sort_destroy = NULL; + + if (priv->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID) + gtk_tree_model_sort_sort (tree_model_sort); + priv->sort_column_id = GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID; +} + +/** + * gtk_tree_model_sort_clear_cache: + * @tree_model_sort: A `GtkTreeModelSort` + * + * This function should almost never be called. It clears the @tree_model_sort + * of any cached iterators that haven’t been reffed with + * gtk_tree_model_ref_node(). This might be useful if the child model being + * sorted is static (and doesn’t change often) and there has been a lot of + * unreffed access to nodes. As a side effect of this function, all unreffed + * iters will be invalid. + * + * Deprecated: 4.10 + **/ +void +gtk_tree_model_sort_clear_cache (GtkTreeModelSort *tree_model_sort) +{ + g_return_if_fail (GTK_IS_TREE_MODEL_SORT (tree_model_sort)); + + if (tree_model_sort->priv->zero_ref_count > 0) + gtk_tree_model_sort_clear_cache_helper (tree_model_sort, (SortLevel *)tree_model_sort->priv->root); +} + +static gboolean +gtk_tree_model_sort_iter_is_valid_helper (GtkTreeIter *iter, + SortLevel *level) +{ + GSequenceIter *siter; + GSequenceIter *end_siter; + + end_siter = g_sequence_get_end_iter (level->seq); + for (siter = g_sequence_get_begin_iter (level->seq); + siter != end_siter; siter = g_sequence_iter_next (siter)) + { + SortElt *elt = g_sequence_get (siter); + + if (iter->user_data == level && iter->user_data2 == elt) + return TRUE; + + if (elt->children) + if (gtk_tree_model_sort_iter_is_valid_helper (iter, elt->children)) + return TRUE; + } + + return FALSE; +} + +/** + * gtk_tree_model_sort_iter_is_valid: + * @tree_model_sort: A `GtkTreeModelSort`. + * @iter: A `GtkTreeIter` + * + * > This function is slow. Only use it for debugging and/or testing + * > purposes. + * + * Checks if the given iter is a valid iter for this `GtkTreeModelSort`. + * + * Returns: %TRUE if the iter is valid, %FALSE if the iter is invalid. + * + * Deprecated: 4.10 + **/ +gboolean +gtk_tree_model_sort_iter_is_valid (GtkTreeModelSort *tree_model_sort, + GtkTreeIter *iter) +{ + g_return_val_if_fail (GTK_IS_TREE_MODEL_SORT (tree_model_sort), FALSE); + g_return_val_if_fail (iter != NULL, FALSE); + + if (!VALID_ITER (iter, tree_model_sort)) + return FALSE; + + return gtk_tree_model_sort_iter_is_valid_helper (iter, + tree_model_sort->priv->root); +} diff --git a/gtk/deprecated/gtktreemodelsort.h b/gtk/deprecated/gtktreemodelsort.h new file mode 100644 index 0000000000..7dc09c29f2 --- /dev/null +++ b/gtk/deprecated/gtktreemodelsort.h @@ -0,0 +1,92 @@ +/* gtktreemodelsort.h + * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#ifndef __GTK_TREE_MODEL_SORT_H__ +#define __GTK_TREE_MODEL_SORT_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_TREE_MODEL_SORT (gtk_tree_model_sort_get_type ()) +#define GTK_TREE_MODEL_SORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_TREE_MODEL_SORT, GtkTreeModelSort)) +#define GTK_TREE_MODEL_SORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_TREE_MODEL_SORT, GtkTreeModelSortClass)) +#define GTK_IS_TREE_MODEL_SORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_TREE_MODEL_SORT)) +#define GTK_IS_TREE_MODEL_SORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_TREE_MODEL_SORT)) +#define GTK_TREE_MODEL_SORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_TREE_MODEL_SORT, GtkTreeModelSortClass)) + +typedef struct _GtkTreeModelSort GtkTreeModelSort; +typedef struct _GtkTreeModelSortClass GtkTreeModelSortClass; +typedef struct _GtkTreeModelSortPrivate GtkTreeModelSortPrivate; + +struct _GtkTreeModelSort +{ + GObject parent; + + /* < private > */ + GtkTreeModelSortPrivate *priv; +}; + +struct _GtkTreeModelSortClass +{ + GObjectClass parent_class; + + /* < private > */ + gpointer padding[8]; +}; + + +GDK_AVAILABLE_IN_ALL +GType gtk_tree_model_sort_get_type (void) G_GNUC_CONST; +GDK_DEPRECATED_IN_4_10 +GtkTreeModel *gtk_tree_model_sort_new_with_model (GtkTreeModel *child_model); + +GDK_DEPRECATED_IN_4_10 +GtkTreeModel *gtk_tree_model_sort_get_model (GtkTreeModelSort *tree_model); +GDK_DEPRECATED_IN_4_10 +GtkTreePath *gtk_tree_model_sort_convert_child_path_to_path (GtkTreeModelSort *tree_model_sort, + GtkTreePath *child_path); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_model_sort_convert_child_iter_to_iter (GtkTreeModelSort *tree_model_sort, + GtkTreeIter *sort_iter, + GtkTreeIter *child_iter); +GDK_DEPRECATED_IN_4_10 +GtkTreePath *gtk_tree_model_sort_convert_path_to_child_path (GtkTreeModelSort *tree_model_sort, + GtkTreePath *sorted_path); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_model_sort_convert_iter_to_child_iter (GtkTreeModelSort *tree_model_sort, + GtkTreeIter *child_iter, + GtkTreeIter *sorted_iter); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_model_sort_reset_default_sort_func (GtkTreeModelSort *tree_model_sort); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_model_sort_clear_cache (GtkTreeModelSort *tree_model_sort); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_model_sort_iter_is_valid (GtkTreeModelSort *tree_model_sort, + GtkTreeIter *iter); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkTreeModelSort, g_object_unref) + +G_END_DECLS + +#endif /* __GTK_TREE_MODEL_SORT_H__ */ diff --git a/gtk/deprecated/gtktreepopover.c b/gtk/deprecated/gtktreepopover.c new file mode 100644 index 0000000000..f2bab158c6 --- /dev/null +++ b/gtk/deprecated/gtktreepopover.c @@ -0,0 +1,912 @@ +/* + * Copyright © 2019 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: Matthias Clasen + */ + +#include "config.h" + +#include "gtktreepopoverprivate.h" + +#include "gtktreemodel.h" +#include "gtkcellarea.h" +#include "gtkcelllayout.h" +#include "gtkcellview.h" +#include "gtkprivate.h" +#include "gtkgizmoprivate.h" +#include "gtkwidgetprivate.h" +#include "gtkbuiltiniconprivate.h" +#include "gtkscrolledwindow.h" +#include "gtkviewport.h" + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + +// TODO +// positioning + sizing + +struct _GtkTreePopover +{ + GtkPopover parent_instance; + + GtkTreeModel *model; + + GtkCellArea *area; + GtkCellAreaContext *context; + + gulong size_changed_id; + gulong row_inserted_id; + gulong row_deleted_id; + gulong row_changed_id; + gulong row_reordered_id; + gulong apply_attributes_id; + + GtkTreeViewRowSeparatorFunc row_separator_func; + gpointer row_separator_data; + GDestroyNotify row_separator_destroy; + + GtkWidget *active_item; +}; + +enum { + PROP_0, + PROP_MODEL, + PROP_CELL_AREA, + + NUM_PROPERTIES +}; + +enum { + MENU_ACTIVATE, + NUM_SIGNALS +}; + +static guint signals[NUM_SIGNALS]; + +static void gtk_tree_popover_cell_layout_init (GtkCellLayoutIface *iface); +static void gtk_tree_popover_set_area (GtkTreePopover *popover, + GtkCellArea *area); +static void rebuild_menu (GtkTreePopover *popover); +static void context_size_changed_cb (GtkCellAreaContext *context, + GParamSpec *pspec, + GtkWidget *popover); +static GtkWidget * gtk_tree_popover_create_item (GtkTreePopover *popover, + GtkTreePath *path, + GtkTreeIter *iter, + gboolean header_item); +static GtkWidget * gtk_tree_popover_get_path_item (GtkTreePopover *popover, + GtkTreePath *search); +static void gtk_tree_popover_set_active_item (GtkTreePopover *popover, + GtkWidget *item); + +G_DEFINE_TYPE_WITH_CODE (GtkTreePopover, gtk_tree_popover, GTK_TYPE_POPOVER, + G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT, + gtk_tree_popover_cell_layout_init)); + +static void +gtk_tree_popover_constructed (GObject *object) +{ + GtkTreePopover *popover = GTK_TREE_POPOVER (object); + + G_OBJECT_CLASS (gtk_tree_popover_parent_class)->constructed (object); + + if (!popover->area) + { + GtkCellArea *area = gtk_cell_area_box_new (); + gtk_tree_popover_set_area (popover, area); + } + + popover->context = gtk_cell_area_create_context (popover->area); + + popover->size_changed_id = g_signal_connect (popover->context, "notify", + G_CALLBACK (context_size_changed_cb), popover); +} + +static void +gtk_tree_popover_dispose (GObject *object) +{ + GtkTreePopover *popover = GTK_TREE_POPOVER (object); + + gtk_tree_popover_set_model (popover, NULL); + gtk_tree_popover_set_area (popover, NULL); + + if (popover->context) + { + g_signal_handler_disconnect (popover->context, popover->size_changed_id); + popover->size_changed_id = 0; + + g_clear_object (&popover->context); + } + + G_OBJECT_CLASS (gtk_tree_popover_parent_class)->dispose (object); +} + +static void +gtk_tree_popover_finalize (GObject *object) +{ + GtkTreePopover *popover = GTK_TREE_POPOVER (object); + + if (popover->row_separator_destroy) + popover->row_separator_destroy (popover->row_separator_data); + + G_OBJECT_CLASS (gtk_tree_popover_parent_class)->finalize (object); +} + +static void +gtk_tree_popover_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkTreePopover *popover = GTK_TREE_POPOVER (object); + + switch (prop_id) + { + case PROP_MODEL: + gtk_tree_popover_set_model (popover, g_value_get_object (value)); + break; + + case PROP_CELL_AREA: + gtk_tree_popover_set_area (popover, g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_tree_popover_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkTreePopover *popover = GTK_TREE_POPOVER (object); + + switch (prop_id) + { + case PROP_MODEL: + g_value_set_object (value, popover->model); + break; + + case PROP_CELL_AREA: + g_value_set_object (value, popover->area); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_tree_popover_class_init (GtkTreePopoverClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + object_class->constructed = gtk_tree_popover_constructed; + object_class->dispose = gtk_tree_popover_dispose; + object_class->finalize = gtk_tree_popover_finalize; + object_class->set_property = gtk_tree_popover_set_property; + object_class->get_property = gtk_tree_popover_get_property; + + g_object_class_install_property (object_class, + PROP_MODEL, + g_param_spec_object ("model", NULL, NULL, + GTK_TYPE_TREE_MODEL, + GTK_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_CELL_AREA, + g_param_spec_object ("cell-area", NULL, NULL, + GTK_TYPE_CELL_AREA, + GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + signals[MENU_ACTIVATE] = + g_signal_new (I_("menu-activate"), + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, + NULL, + G_TYPE_NONE, 1, G_TYPE_STRING); +} + +static GtkWidget * +gtk_tree_popover_get_stack (GtkTreePopover *popover) +{ + GtkWidget *sw = gtk_popover_get_child (GTK_POPOVER (popover)); + GtkWidget *vp = gtk_scrolled_window_get_child (GTK_SCROLLED_WINDOW (sw)); + GtkWidget *stack = gtk_viewport_get_child (GTK_VIEWPORT (vp)); + + return stack; +} + +static void +gtk_tree_popover_add_submenu (GtkTreePopover *popover, + GtkWidget *submenu, + const char *name) +{ + GtkWidget *stack = gtk_tree_popover_get_stack (popover); + gtk_stack_add_named (GTK_STACK (stack), submenu, name); +} + +static GtkWidget * +gtk_tree_popover_get_submenu (GtkTreePopover *popover, + const char *name) +{ + GtkWidget *stack = gtk_tree_popover_get_stack (popover); + return gtk_stack_get_child_by_name (GTK_STACK (stack), name); +} + +void +gtk_tree_popover_open_submenu (GtkTreePopover *popover, + const char *name) +{ + GtkWidget *stack = gtk_tree_popover_get_stack (popover); + gtk_stack_set_visible_child_name (GTK_STACK (stack), name); +} + +static void +gtk_tree_popover_init (GtkTreePopover *popover) +{ + GtkWidget *sw; + GtkWidget *stack; + + sw = gtk_scrolled_window_new (); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_propagate_natural_height (GTK_SCROLLED_WINDOW (sw), TRUE); + gtk_popover_set_child (GTK_POPOVER (popover), sw); + + stack = gtk_stack_new (); + gtk_stack_set_vhomogeneous (GTK_STACK (stack), FALSE); + gtk_stack_set_transition_type (GTK_STACK (stack), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT); + gtk_stack_set_interpolate_size (GTK_STACK (stack), TRUE); + gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), stack); + + gtk_widget_add_css_class (GTK_WIDGET (popover), "menu"); +} + +static GtkCellArea * +gtk_tree_popover_cell_layout_get_area (GtkCellLayout *layout) +{ + return GTK_TREE_POPOVER (layout)->area; +} + +static void +gtk_tree_popover_cell_layout_init (GtkCellLayoutIface *iface) +{ + iface->get_area = gtk_tree_popover_cell_layout_get_area; +} + +static void +insert_at_position (GtkBox *box, + GtkWidget *child, + int position) +{ + GtkWidget *sibling = NULL; + + if (position > 0) + { + int i; + + sibling = gtk_widget_get_first_child (GTK_WIDGET (box)); + for (i = 1; i < position; i++) + sibling = gtk_widget_get_next_sibling (sibling); + } + + gtk_box_insert_child_after (box, child, sibling); +} + +static GtkWidget * +ensure_submenu (GtkTreePopover *popover, + GtkTreePath *path) +{ + GtkWidget *box; + char *name; + + if (path) + name = gtk_tree_path_to_string (path); + else + name = NULL; + + box = gtk_tree_popover_get_submenu (popover, name ? name : "main"); + if (!box) + { + box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + gtk_tree_popover_add_submenu (popover, box, name ? name : "main"); + if (path) + { + GtkTreeIter iter; + GtkWidget *item; + gtk_tree_model_get_iter (popover->model, &iter, path); + item = gtk_tree_popover_create_item (popover, path, &iter, TRUE); + gtk_box_append (GTK_BOX (box), item); + gtk_box_append (GTK_BOX (box), gtk_separator_new (GTK_ORIENTATION_HORIZONTAL)); + } + + } + + g_free (name); + + return box; +} + +static void +row_inserted_cb (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + GtkTreePopover *popover) +{ + int *indices, depth, index; + GtkWidget *item; + GtkWidget *box; + + indices = gtk_tree_path_get_indices (path); + depth = gtk_tree_path_get_depth (path); + index = indices[depth - 1]; + + item = gtk_tree_popover_create_item (popover, path, iter, FALSE); + if (depth == 1) + { + box = ensure_submenu (popover, NULL); + insert_at_position (GTK_BOX (box), item, index); + } + else + { + GtkTreePath *ppath; + + ppath = gtk_tree_path_copy (path); + gtk_tree_path_up (ppath); + + box = ensure_submenu (popover, ppath); + insert_at_position (GTK_BOX (box), item, index + 2); + + gtk_tree_path_free (ppath); + } + + gtk_cell_area_context_reset (popover->context); +} + +static void +row_deleted_cb (GtkTreeModel *model, + GtkTreePath *path, + GtkTreePopover *popover) +{ + GtkWidget *item; + + item = gtk_tree_popover_get_path_item (popover, path); + + if (item) + { + gtk_widget_unparent (item); + gtk_cell_area_context_reset (popover->context); + } +} + +static void +row_changed_cb (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + GtkTreePopover *popover) +{ + gboolean is_separator = FALSE; + GtkWidget *item; + int *indices, depth, index; + + item = gtk_tree_popover_get_path_item (popover, path); + + if (!item) + return; + + indices = gtk_tree_path_get_indices (path); + depth = gtk_tree_path_get_depth (path); + index = indices[depth - 1]; + + if (popover->row_separator_func) + is_separator = popover->row_separator_func (model, iter, popover->row_separator_data); + + if (is_separator != GTK_IS_SEPARATOR (item)) + { + GtkWidget *box = gtk_widget_get_parent (item); + + gtk_box_remove (GTK_BOX (box), item); + + item = gtk_tree_popover_create_item (popover, path, iter, FALSE); + + if (depth == 1) + insert_at_position (GTK_BOX (box), item, index); + else + insert_at_position (GTK_BOX (box), item, index + 2); + } +} + +static void +row_reordered_cb (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + int *new_order, + GtkTreePopover *popover) +{ + rebuild_menu (popover); +} + +static void +context_size_changed_cb (GtkCellAreaContext *context, + GParamSpec *pspec, + GtkWidget *popover) +{ + if (!strcmp (pspec->name, "minimum-width") || + !strcmp (pspec->name, "natural-width") || + !strcmp (pspec->name, "minimum-height") || + !strcmp (pspec->name, "natural-height")) + gtk_widget_queue_resize (popover); +} + +static gboolean +area_is_sensitive (GtkCellArea *area) +{ + GList *cells, *list; + gboolean sensitive = FALSE; + + cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (area)); + + for (list = cells; list; list = list->next) + { + g_object_get (list->data, "sensitive", &sensitive, NULL); + + if (sensitive) + break; + } + g_list_free (cells); + + return sensitive; +} + +static GtkWidget * +gtk_tree_popover_get_path_item (GtkTreePopover *popover, + GtkTreePath *search) +{ + GtkWidget *stack = gtk_tree_popover_get_stack (popover); + GtkWidget *item = NULL; + GtkWidget *stackchild; + GtkWidget *child; + + for (stackchild = gtk_widget_get_first_child (stack); + stackchild != NULL; + stackchild = gtk_widget_get_next_sibling (stackchild)) + { + for (child = gtk_widget_get_first_child (stackchild); + !item && child; + child = gtk_widget_get_next_sibling (child)) + { + GtkTreePath *path = NULL; + + if (GTK_IS_SEPARATOR (child)) + { + GtkTreeRowReference *row = g_object_get_data (G_OBJECT (child), "gtk-tree-path"); + + if (row) + { + path = gtk_tree_row_reference_get_path (row); + if (!path) + item = child; + } + } + else + { + GtkWidget *view = GTK_WIDGET (g_object_get_data (G_OBJECT (child), "view")); + + path = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (view)); + + if (!path) + item = child; + } + + if (path) + { + if (gtk_tree_path_compare (search, path) == 0) + item = child; + gtk_tree_path_free (path); + } + } + } + + return item; +} + +static void +area_apply_attributes_cb (GtkCellArea *area, + GtkTreeModel *tree_model, + GtkTreeIter *iter, + gboolean is_expander, + gboolean is_expanded, + GtkTreePopover *popover) +{ + GtkTreePath*path; + GtkWidget *item; + gboolean sensitive; + GtkTreeIter dummy; + gboolean has_submenu = FALSE; + + if (gtk_tree_model_iter_children (popover->model, &dummy, iter)) + has_submenu = TRUE; + + path = gtk_tree_model_get_path (tree_model, iter); + item = gtk_tree_popover_get_path_item (popover, path); + + if (item) + { + sensitive = area_is_sensitive (popover->area); + gtk_widget_set_sensitive (item, sensitive || has_submenu); + } + + gtk_tree_path_free (path); +} + +static void +gtk_tree_popover_set_area (GtkTreePopover *popover, + GtkCellArea *area) +{ + if (popover->area) + { + g_signal_handler_disconnect (popover->area, popover->apply_attributes_id); + popover->apply_attributes_id = 0; + g_clear_object (&popover->area); + } + + popover->area = area; + + if (popover->area) + { + g_object_ref_sink (popover->area); + popover->apply_attributes_id = g_signal_connect (popover->area, "apply-attributes", + G_CALLBACK (area_apply_attributes_cb), popover); + } +} + +static void +activate_item (GtkWidget *item, + GtkTreePopover *popover) +{ + GtkCellView *view; + GtkTreePath *path; + char *path_str; + gboolean is_header = FALSE; + gboolean has_submenu = FALSE; + + is_header = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item), "is-header")); + + view = GTK_CELL_VIEW (g_object_get_data (G_OBJECT (item), "view")); + + path = gtk_cell_view_get_displayed_row (view); + + if (is_header) + { + gtk_tree_path_up (path); + } + else + { + GtkTreeIter iter; + GtkTreeIter dummy; + + gtk_tree_model_get_iter (popover->model, &iter, path); + if (gtk_tree_model_iter_children (popover->model, &dummy, &iter)) + has_submenu = TRUE; + } + + path_str = gtk_tree_path_to_string (path); + + if (is_header || has_submenu) + { + gtk_tree_popover_open_submenu (popover, path_str ? path_str : "main"); + } + else + { + g_signal_emit (popover, signals[MENU_ACTIVATE], 0, path_str); + gtk_popover_popdown (GTK_POPOVER (popover)); + } + + g_free (path_str); + gtk_tree_path_free (path); +} + +static void +item_activated_cb (GtkGesture *gesture, + guint n_press, + double x, + double y, + GtkTreePopover *popover) +{ + GtkWidget *item = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture)); + activate_item (item, popover); +} + +static void +enter_cb (GtkEventController *controller, + double x, + double y, + GtkTreePopover *popover) +{ + GtkWidget *item; + item = gtk_event_controller_get_widget (controller); + + gtk_tree_popover_set_active_item (popover, item); +} + +static void +enter_focus_cb (GtkEventController *controller, + GtkTreePopover *popover) +{ + GtkWidget *item = gtk_event_controller_get_widget (controller); + + gtk_tree_popover_set_active_item (popover, item); +} + +static gboolean +activate_shortcut (GtkWidget *widget, + GVariant *args, + gpointer user_data) +{ + activate_item (widget, user_data); + return TRUE; +} + +static GtkWidget * +gtk_tree_popover_create_item (GtkTreePopover *popover, + GtkTreePath *path, + GtkTreeIter *iter, + gboolean header_item) +{ + GtkWidget *item, *view; + gboolean is_separator = FALSE; + + if (popover->row_separator_func) + is_separator = popover->row_separator_func (popover->model, iter, popover->row_separator_data); + + if (is_separator) + { + item = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL); + g_object_set_data_full (G_OBJECT (item), "gtk-tree-path", + gtk_tree_row_reference_new (popover->model, path), + (GDestroyNotify)gtk_tree_row_reference_free); + } + else + { + GtkEventController *controller; + GtkTreeIter dummy; + gboolean has_submenu = FALSE; + GtkWidget *indicator; + + if (!header_item && + gtk_tree_model_iter_children (popover->model, &dummy, iter)) + has_submenu = TRUE; + + view = gtk_cell_view_new_with_context (popover->area, popover->context); + gtk_cell_view_set_model (GTK_CELL_VIEW (view), popover->model); + gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (view), path); + gtk_widget_set_hexpand (view, TRUE); + + item = gtk_gizmo_new ("modelbutton", NULL, NULL, NULL, NULL, + (GtkGizmoFocusFunc)gtk_widget_focus_self, + (GtkGizmoGrabFocusFunc)gtk_widget_grab_focus_self); + gtk_widget_set_layout_manager (item, gtk_box_layout_new (GTK_ORIENTATION_HORIZONTAL)); + gtk_widget_set_focusable (item, TRUE); + gtk_widget_add_css_class (item, "flat"); + + if (header_item) + { + indicator = gtk_builtin_icon_new ("arrow"); + gtk_widget_add_css_class (indicator, "left"); + gtk_widget_set_parent (indicator, item); + } + + gtk_widget_set_parent (view, item); + + indicator = gtk_builtin_icon_new (has_submenu ? "arrow" : "none"); + gtk_widget_add_css_class (indicator, "right"); + gtk_widget_set_parent (indicator, item); + + controller = GTK_EVENT_CONTROLLER (gtk_gesture_click_new ()); + g_signal_connect (controller, "pressed", G_CALLBACK (item_activated_cb), popover); + gtk_widget_add_controller (item, GTK_EVENT_CONTROLLER (controller)); + + controller = gtk_event_controller_motion_new (); + g_signal_connect (controller, "enter", G_CALLBACK (enter_cb), popover); + gtk_widget_add_controller (item, controller); + + controller = gtk_event_controller_focus_new (); + g_signal_connect (controller, "enter", G_CALLBACK (enter_focus_cb), popover); + gtk_widget_add_controller (item, controller); + + { + const guint activate_keyvals[] = { GDK_KEY_space, GDK_KEY_KP_Space, + GDK_KEY_Return, GDK_KEY_ISO_Enter, + GDK_KEY_KP_Enter }; + GtkShortcutTrigger *trigger; + GtkShortcut *shortcut; + + trigger = g_object_ref (gtk_never_trigger_get ()); + for (int i = 0; i < G_N_ELEMENTS (activate_keyvals); i++) + trigger = gtk_alternative_trigger_new (gtk_keyval_trigger_new (activate_keyvals[i], 0), trigger); + + shortcut = gtk_shortcut_new (trigger, gtk_callback_action_new (activate_shortcut, popover, NULL)); + controller = gtk_shortcut_controller_new (); + gtk_shortcut_controller_add_shortcut (GTK_SHORTCUT_CONTROLLER (controller), shortcut); + gtk_widget_add_controller (item, controller); + } + + g_object_set_data (G_OBJECT (item), "is-header", GINT_TO_POINTER (header_item)); + g_object_set_data (G_OBJECT (item), "view", view); + } + + return item; +} + +static void +populate (GtkTreePopover *popover, + GtkTreeIter *parent) +{ + GtkTreeIter iter; + gboolean valid = FALSE; + + if (!popover->model) + return; + + valid = gtk_tree_model_iter_children (popover->model, &iter, parent); + + while (valid) + { + GtkTreePath *path; + + path = gtk_tree_model_get_path (popover->model, &iter); + row_inserted_cb (popover->model, path, &iter, popover); + + populate (popover, &iter); + + valid = gtk_tree_model_iter_next (popover->model, &iter); + gtk_tree_path_free (path); + } +} + +static void +gtk_tree_popover_populate (GtkTreePopover *popover) +{ + populate (popover, NULL); +} + +static void +rebuild_menu (GtkTreePopover *popover) +{ + GtkWidget *stack; + GtkWidget *child; + + stack = gtk_tree_popover_get_stack (popover); + while ((child = gtk_widget_get_first_child (stack))) + gtk_stack_remove (GTK_STACK (stack), child); + + if (popover->model) + gtk_tree_popover_populate (popover); +} + +void +gtk_tree_popover_set_model (GtkTreePopover *popover, + GtkTreeModel *model) +{ + if (popover->model == model) + return; + + if (popover->model) + { + g_signal_handler_disconnect (popover->model, popover->row_inserted_id); + g_signal_handler_disconnect (popover->model, popover->row_deleted_id); + g_signal_handler_disconnect (popover->model, popover->row_changed_id); + g_signal_handler_disconnect (popover->model, popover->row_reordered_id); + popover->row_inserted_id = 0; + popover->row_deleted_id = 0; + popover->row_changed_id = 0; + popover->row_reordered_id = 0; + + g_object_unref (popover->model); + } + + popover->model = model; + + if (popover->model) + { + g_object_ref (popover->model); + + popover->row_inserted_id = g_signal_connect (popover->model, "row-inserted", + G_CALLBACK (row_inserted_cb), popover); + popover->row_deleted_id = g_signal_connect (popover->model, "row-deleted", + G_CALLBACK (row_deleted_cb), popover); + popover->row_changed_id = g_signal_connect (popover->model, "row-changed", + G_CALLBACK (row_changed_cb), popover); + popover->row_reordered_id = g_signal_connect (popover->model, "rows-reordered", + G_CALLBACK (row_reordered_cb), popover); + } + + rebuild_menu (popover); +} + +void +gtk_tree_popover_set_row_separator_func (GtkTreePopover *popover, + GtkTreeViewRowSeparatorFunc func, + gpointer data, + GDestroyNotify destroy) +{ + if (popover->row_separator_destroy) + popover->row_separator_destroy (popover->row_separator_data); + + popover->row_separator_func = func; + popover->row_separator_data = data; + popover->row_separator_destroy = destroy; + + rebuild_menu (popover); +} + +static void +gtk_tree_popover_set_active_item (GtkTreePopover *popover, + GtkWidget *item) +{ + if (popover->active_item == item) + return; + + if (popover->active_item) + { + gtk_widget_unset_state_flags (popover->active_item, GTK_STATE_FLAG_SELECTED); + g_object_remove_weak_pointer (G_OBJECT (popover->active_item), (gpointer *)&popover->active_item); + } + + popover->active_item = item; + + if (popover->active_item) + { + g_object_add_weak_pointer (G_OBJECT (popover->active_item), (gpointer *)&popover->active_item); + gtk_widget_set_state_flags (popover->active_item, GTK_STATE_FLAG_SELECTED, FALSE); + } +} + +void +gtk_tree_popover_set_active (GtkTreePopover *popover, + int item) +{ + GtkWidget *box; + GtkWidget *child; + int pos; + + if (item == -1) + { + gtk_tree_popover_set_active_item (popover, NULL); + return; + } + + box = gtk_tree_popover_get_submenu (popover, "main"); + if (!box) + return; + + for (child = gtk_widget_get_first_child (box), pos = 0; + child; + child = gtk_widget_get_next_sibling (child), pos++) + { + if (pos == item) + { + gtk_tree_popover_set_active_item (popover, child); + break; + } + } +} + diff --git a/gtk/deprecated/gtktreepopoverprivate.h b/gtk/deprecated/gtktreepopoverprivate.h new file mode 100644 index 0000000000..2656f90a1d --- /dev/null +++ b/gtk/deprecated/gtktreepopoverprivate.h @@ -0,0 +1,43 @@ +/* + * Copyright © 2019 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: Matthias Clasen + */ + +#ifndef __GTK_TREE_POPOVER_PRIVATE_H__ +#define __GTK_TREE_POPOVER_PRIVATE_H__ + +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_TREE_POPOVER (gtk_tree_popover_get_type ()) +G_DECLARE_FINAL_TYPE (GtkTreePopover, gtk_tree_popover, GTK, TREE_POPOVER, GtkPopover) + +void gtk_tree_popover_set_model (GtkTreePopover *popover, + GtkTreeModel *model); +void gtk_tree_popover_set_row_separator_func (GtkTreePopover *popover, + GtkTreeViewRowSeparatorFunc func, + gpointer data, + GDestroyNotify destroy); +void gtk_tree_popover_set_active (GtkTreePopover *popover, + int item); +void gtk_tree_popover_open_submenu (GtkTreePopover *popover, + const char *name); + +G_END_DECLS + +#endif /* __GTK_TREE_POPOVER_PRIVATE_H__ */ diff --git a/gtk/deprecated/gtktreeprivate.h b/gtk/deprecated/gtktreeprivate.h new file mode 100644 index 0000000000..52267349ab --- /dev/null +++ b/gtk/deprecated/gtktreeprivate.h @@ -0,0 +1,146 @@ +/* gtktreeprivate.h + * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#ifndef __GTK_TREE_PRIVATE_H__ +#define __GTK_TREE_PRIVATE_H__ + + +#include +#include +#include + +G_BEGIN_DECLS + +#define TREE_VIEW_DRAG_WIDTH 6 + +typedef enum +{ + GTK_TREE_SELECT_MODE_TOGGLE = 1 << 0, + GTK_TREE_SELECT_MODE_EXTEND = 1 << 1 +} +GtkTreeSelectMode; + +/* functions that shouldn't be exported */ +void _gtk_tree_selection_internal_select_node (GtkTreeSelection *selection, + GtkTreeRBNode *node, + GtkTreeRBTree *tree, + GtkTreePath *path, + GtkTreeSelectMode mode, + gboolean override_browse_mode); +void _gtk_tree_selection_emit_changed (GtkTreeSelection *selection); +gboolean _gtk_tree_view_find_node (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeRBTree **tree, + GtkTreeRBNode **node); +gboolean _gtk_tree_view_get_cursor_node (GtkTreeView *tree_view, + GtkTreeRBTree **tree, + GtkTreeRBNode **node); +GtkTreePath *_gtk_tree_path_new_from_rbtree (GtkTreeRBTree *tree, + GtkTreeRBNode *node); + +void _gtk_tree_view_add_editable (GtkTreeView *tree_view, + GtkTreeViewColumn *column, + GtkTreePath *path, + GtkCellEditable *cell_editable, + GdkRectangle *cell_area); +void _gtk_tree_view_remove_editable (GtkTreeView *tree_view, + GtkTreeViewColumn *column, + GtkCellEditable *cell_editable); + +void _gtk_tree_view_install_mark_rows_col_dirty (GtkTreeView *tree_view, + gboolean install_handler); +void _gtk_tree_view_column_autosize (GtkTreeView *tree_view, + GtkTreeViewColumn *column); + +void _gtk_tree_view_get_row_separator_func (GtkTreeView *tree_view, + GtkTreeViewRowSeparatorFunc *func, + gpointer *data); +GtkTreePath *_gtk_tree_view_get_anchor_path (GtkTreeView *tree_view); +void _gtk_tree_view_set_anchor_path (GtkTreeView *tree_view, + GtkTreePath *anchor_path); +GtkTreeRBTree * _gtk_tree_view_get_rbtree (GtkTreeView *tree_view); + +GtkTreeViewColumn *_gtk_tree_view_get_focus_column (GtkTreeView *tree_view); +void _gtk_tree_view_set_focus_column (GtkTreeView *tree_view, + GtkTreeViewColumn *column); + +GtkTreeSelection* _gtk_tree_selection_new (void); +GtkTreeSelection* _gtk_tree_selection_new_with_tree_view (GtkTreeView *tree_view); +void _gtk_tree_selection_set_tree_view (GtkTreeSelection *selection, + GtkTreeView *tree_view); +gboolean _gtk_tree_selection_row_is_selectable (GtkTreeSelection *selection, + GtkTreeRBNode *node, + GtkTreePath *path); + + +void _gtk_tree_view_column_realize_button (GtkTreeViewColumn *column); + +void _gtk_tree_view_column_set_tree_view (GtkTreeViewColumn *column, + GtkTreeView *tree_view); +int _gtk_tree_view_column_request_width (GtkTreeViewColumn *tree_column); +void _gtk_tree_view_column_allocate (GtkTreeViewColumn *tree_column, + int x_offset, + int width, + int height); +void _gtk_tree_view_column_unset_model (GtkTreeViewColumn *column, + GtkTreeModel *old_model); +void _gtk_tree_view_column_unset_tree_view (GtkTreeViewColumn *column); +void _gtk_tree_view_column_start_drag (GtkTreeView *tree_view, + GtkTreeViewColumn *column, + GdkDevice *device); +gboolean _gtk_tree_view_column_cell_event (GtkTreeViewColumn *tree_column, + GdkEvent *event, + const GdkRectangle *cell_area, + guint flags); +gboolean _gtk_tree_view_column_has_editable_cell(GtkTreeViewColumn *column); +GtkCellRenderer *_gtk_tree_view_column_get_edited_cell (GtkTreeViewColumn *column); +GtkCellRenderer *_gtk_tree_view_column_get_cell_at_pos (GtkTreeViewColumn *column, + GdkRectangle *cell_area, + GdkRectangle *background_area, + int x, + int y); +gboolean _gtk_tree_view_column_is_blank_at_pos (GtkTreeViewColumn *column, + GdkRectangle *cell_area, + GdkRectangle *background_area, + int x, + int y); + +void gtk_tree_view_column_cell_snapshot (GtkTreeViewColumn *tree_column, + GtkSnapshot *snapshot, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + guint flags, + gboolean draw_focus); +void _gtk_tree_view_column_cell_set_dirty (GtkTreeViewColumn *tree_column, + gboolean install_handler); +gboolean _gtk_tree_view_column_cell_get_dirty (GtkTreeViewColumn *tree_column); + +void _gtk_tree_view_column_push_padding (GtkTreeViewColumn *column, + int padding); +int _gtk_tree_view_column_get_requested_width (GtkTreeViewColumn *column); +int _gtk_tree_view_column_get_drag_x (GtkTreeViewColumn *column); +GtkCellAreaContext *_gtk_tree_view_column_get_context (GtkTreeViewColumn *column); +gboolean _gtk_tree_view_column_coords_in_resize_rect (GtkTreeViewColumn *column, + double x, + double y); + + +G_END_DECLS + + +#endif /* __GTK_TREE_PRIVATE_H__ */ + diff --git a/gtk/deprecated/gtktreerbtree.c b/gtk/deprecated/gtktreerbtree.c new file mode 100644 index 0000000000..6630248b43 --- /dev/null +++ b/gtk/deprecated/gtktreerbtree.c @@ -0,0 +1,1747 @@ +/* gtktreerbtree.c + * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#include "config.h" +#include "gtktreerbtreeprivate.h" +#include "gtkdebug.h" + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + +static GtkTreeRBNode *gtk_tree_rbnode_new (GtkTreeRBTree *tree, + int height); +static void gtk_tree_rbnode_free (GtkTreeRBNode *node); +static void gtk_tree_rbnode_rotate_left (GtkTreeRBTree *tree, + GtkTreeRBNode *node); +static void gtk_tree_rbnode_rotate_right (GtkTreeRBTree *tree, + GtkTreeRBNode *node); +static void gtk_tree_rbtree_insert_fixup (GtkTreeRBTree *tree, + GtkTreeRBNode *node); +static void gtk_tree_rbtree_remove_node_fixup (GtkTreeRBTree *tree, + GtkTreeRBNode *node, + GtkTreeRBNode *parent); +static inline void fixup_validation (GtkTreeRBTree *tree, + GtkTreeRBNode *node); +static inline void fixup_total_count (GtkTreeRBTree *tree, + GtkTreeRBNode *node); +#ifdef G_ENABLE_DEBUG +static void gtk_tree_rbtree_test (const char *where, + GtkTreeRBTree *tree); +static void gtk_tree_rbtree_debug_spew (GtkTreeRBTree *tree, + GString *s); +#endif + +static const GtkTreeRBNode nil = +{ + /* .flags = */ GTK_TREE_RBNODE_BLACK, + + /* rest is NULL */ +}; + +gboolean +gtk_tree_rbtree_is_nil (GtkTreeRBNode *node) +{ + return node == &nil; +} + +static GtkTreeRBNode * +gtk_tree_rbnode_new (GtkTreeRBTree *tree, + int height) +{ + GtkTreeRBNode *node = g_slice_new (GtkTreeRBNode); + + node->left = (GtkTreeRBNode *) &nil; + node->right = (GtkTreeRBNode *) &nil; + node->parent = (GtkTreeRBNode *) &nil; + node->flags = GTK_TREE_RBNODE_RED; + node->total_count = 1; + node->count = 1; + node->children = NULL; + node->offset = height; + return node; +} + +static void +gtk_tree_rbnode_free (GtkTreeRBNode *node) +{ +#ifdef G_ENABLE_DEBUG + if (GTK_DEBUG_CHECK (TREE)) + { + node->left = (gpointer) 0xdeadbeef; + node->right = (gpointer) 0xdeadbeef; + node->parent = (gpointer) 0xdeadbeef; + node->total_count = 56789; + node->offset = 56789; + node->count = 56789; + node->flags = 0; + } +#endif + g_slice_free (GtkTreeRBNode, node); +} + +static void +gtk_tree_rbnode_rotate_left (GtkTreeRBTree *tree, + GtkTreeRBNode *node) +{ + int node_height, right_height; + GtkTreeRBNode *right; + + g_return_if_fail (!gtk_tree_rbtree_is_nil (node)); + g_return_if_fail (!gtk_tree_rbtree_is_nil (node->right)); + + right = node->right; + + node_height = GTK_TREE_RBNODE_GET_HEIGHT (node); + right_height = GTK_TREE_RBNODE_GET_HEIGHT (right); + node->right = right->left; + if (!gtk_tree_rbtree_is_nil (right->left)) + right->left->parent = node; + + right->parent = node->parent; + if (!gtk_tree_rbtree_is_nil (node->parent)) + { + if (node == node->parent->left) + node->parent->left = right; + else + node->parent->right = right; + } + else + { + tree->root = right; + } + + right->left = node; + node->parent = right; + + node->count = 1 + node->left->count + node->right->count; + right->count = 1 + right->left->count + right->right->count; + + node->offset = node_height + node->left->offset + node->right->offset + + (node->children ? node->children->root->offset : 0); + right->offset = right_height + right->left->offset + right->right->offset + + (right->children ? right->children->root->offset : 0); + + fixup_validation (tree, node); + fixup_validation (tree, right); + fixup_total_count (tree, node); + fixup_total_count (tree, right); +} + +static void +gtk_tree_rbnode_rotate_right (GtkTreeRBTree *tree, + GtkTreeRBNode *node) +{ + int node_height, left_height; + GtkTreeRBNode *left; + + g_return_if_fail (!gtk_tree_rbtree_is_nil (node)); + g_return_if_fail (!gtk_tree_rbtree_is_nil (node->left)); + + left = node->left; + + node_height = GTK_TREE_RBNODE_GET_HEIGHT (node); + left_height = GTK_TREE_RBNODE_GET_HEIGHT (left); + + node->left = left->right; + if (!gtk_tree_rbtree_is_nil (left->right)) + left->right->parent = node; + + left->parent = node->parent; + if (!gtk_tree_rbtree_is_nil (node->parent)) + { + if (node == node->parent->right) + node->parent->right = left; + else + node->parent->left = left; + } + else + { + tree->root = left; + } + + /* link node and left */ + left->right = node; + node->parent = left; + + node->count = 1 + node->left->count + node->right->count; + left->count = 1 + left->left->count + left->right->count; + + node->offset = node_height + node->left->offset + node->right->offset + + (node->children ? node->children->root->offset : 0); + left->offset = left_height + left->left->offset + left->right->offset + + (left->children ? left->children->root->offset : 0); + + fixup_validation (tree, node); + fixup_validation (tree, left); + fixup_total_count (tree, node); + fixup_total_count (tree, left); +} + +static void +gtk_tree_rbtree_insert_fixup (GtkTreeRBTree *tree, + GtkTreeRBNode *node) +{ + /* check Red-Black properties */ + while (node != tree->root && GTK_TREE_RBNODE_GET_COLOR (node->parent) == GTK_TREE_RBNODE_RED) + { + /* we have a violation */ + if (node->parent == node->parent->parent->left) + { + GtkTreeRBNode *y = node->parent->parent->right; + if (GTK_TREE_RBNODE_GET_COLOR (y) == GTK_TREE_RBNODE_RED) + { + /* uncle is GTK_TREE_RBNODE_RED */ + GTK_TREE_RBNODE_SET_COLOR (node->parent, GTK_TREE_RBNODE_BLACK); + GTK_TREE_RBNODE_SET_COLOR (y, GTK_TREE_RBNODE_BLACK); + GTK_TREE_RBNODE_SET_COLOR (node->parent->parent, GTK_TREE_RBNODE_RED); + node = node->parent->parent; + } + else + { + /* uncle is GTK_TREE_RBNODE_BLACK */ + if (node == node->parent->right) + { + /* make node a left child */ + node = node->parent; + gtk_tree_rbnode_rotate_left (tree, node); + } + + /* recolor and rotate */ + GTK_TREE_RBNODE_SET_COLOR (node->parent, GTK_TREE_RBNODE_BLACK); + GTK_TREE_RBNODE_SET_COLOR (node->parent->parent, GTK_TREE_RBNODE_RED); + gtk_tree_rbnode_rotate_right (tree, node->parent->parent); + } + } + else + { + /* mirror image of above code */ + GtkTreeRBNode *y = node->parent->parent->left; + if (GTK_TREE_RBNODE_GET_COLOR (y) == GTK_TREE_RBNODE_RED) + { + /* uncle is GTK_TREE_RBNODE_RED */ + GTK_TREE_RBNODE_SET_COLOR (node->parent, GTK_TREE_RBNODE_BLACK); + GTK_TREE_RBNODE_SET_COLOR (y, GTK_TREE_RBNODE_BLACK); + GTK_TREE_RBNODE_SET_COLOR (node->parent->parent, GTK_TREE_RBNODE_RED); + node = node->parent->parent; + } + else + { + /* uncle is GTK_TREE_RBNODE_BLACK */ + if (node == node->parent->left) + { + node = node->parent; + gtk_tree_rbnode_rotate_right (tree, node); + } + GTK_TREE_RBNODE_SET_COLOR (node->parent, GTK_TREE_RBNODE_BLACK); + GTK_TREE_RBNODE_SET_COLOR (node->parent->parent, GTK_TREE_RBNODE_RED); + gtk_tree_rbnode_rotate_left (tree, node->parent->parent); + } + } + } + GTK_TREE_RBNODE_SET_COLOR (tree->root, GTK_TREE_RBNODE_BLACK); +} + +static void +gtk_tree_rbtree_remove_node_fixup (GtkTreeRBTree *tree, + GtkTreeRBNode *node, + GtkTreeRBNode *parent) +{ + while (node != tree->root && GTK_TREE_RBNODE_GET_COLOR (node) == GTK_TREE_RBNODE_BLACK) + { + if (node == parent->left) + { + GtkTreeRBNode *w = parent->right; + if (GTK_TREE_RBNODE_GET_COLOR (w) == GTK_TREE_RBNODE_RED) + { + GTK_TREE_RBNODE_SET_COLOR (w, GTK_TREE_RBNODE_BLACK); + GTK_TREE_RBNODE_SET_COLOR (parent, GTK_TREE_RBNODE_RED); + gtk_tree_rbnode_rotate_left (tree, parent); + w = parent->right; + } + g_assert (w); + if (GTK_TREE_RBNODE_GET_COLOR (w->left) == GTK_TREE_RBNODE_BLACK && GTK_TREE_RBNODE_GET_COLOR (w->right) == GTK_TREE_RBNODE_BLACK) + { + GTK_TREE_RBNODE_SET_COLOR (w, GTK_TREE_RBNODE_RED); + node = parent; + } + else + { + if (GTK_TREE_RBNODE_GET_COLOR (w->right) == GTK_TREE_RBNODE_BLACK) + { + GTK_TREE_RBNODE_SET_COLOR (w->left, GTK_TREE_RBNODE_BLACK); + GTK_TREE_RBNODE_SET_COLOR (w, GTK_TREE_RBNODE_RED); + gtk_tree_rbnode_rotate_right (tree, w); + w = parent->right; + } + GTK_TREE_RBNODE_SET_COLOR (w, GTK_TREE_RBNODE_GET_COLOR (parent)); + GTK_TREE_RBNODE_SET_COLOR (parent, GTK_TREE_RBNODE_BLACK); + GTK_TREE_RBNODE_SET_COLOR (w->right, GTK_TREE_RBNODE_BLACK); + gtk_tree_rbnode_rotate_left (tree, parent); + node = tree->root; + } + } + else + { + GtkTreeRBNode *w = parent->left; + if (GTK_TREE_RBNODE_GET_COLOR (w) == GTK_TREE_RBNODE_RED) + { + GTK_TREE_RBNODE_SET_COLOR (w, GTK_TREE_RBNODE_BLACK); + GTK_TREE_RBNODE_SET_COLOR (parent, GTK_TREE_RBNODE_RED); + gtk_tree_rbnode_rotate_right (tree, parent); + w = parent->left; + } + g_assert (w); + if (GTK_TREE_RBNODE_GET_COLOR (w->right) == GTK_TREE_RBNODE_BLACK && GTK_TREE_RBNODE_GET_COLOR (w->left) == GTK_TREE_RBNODE_BLACK) + { + GTK_TREE_RBNODE_SET_COLOR (w, GTK_TREE_RBNODE_RED); + node = parent; + } + else + { + if (GTK_TREE_RBNODE_GET_COLOR (w->left) == GTK_TREE_RBNODE_BLACK) + { + GTK_TREE_RBNODE_SET_COLOR (w->right, GTK_TREE_RBNODE_BLACK); + GTK_TREE_RBNODE_SET_COLOR (w, GTK_TREE_RBNODE_RED); + gtk_tree_rbnode_rotate_left (tree, w); + w = parent->left; + } + GTK_TREE_RBNODE_SET_COLOR (w, GTK_TREE_RBNODE_GET_COLOR (parent)); + GTK_TREE_RBNODE_SET_COLOR (parent, GTK_TREE_RBNODE_BLACK); + GTK_TREE_RBNODE_SET_COLOR (w->left, GTK_TREE_RBNODE_BLACK); + gtk_tree_rbnode_rotate_right (tree, parent); + node = tree->root; + } + } + + parent = node->parent; + } + GTK_TREE_RBNODE_SET_COLOR (node, GTK_TREE_RBNODE_BLACK); +} + +GtkTreeRBTree * +gtk_tree_rbtree_new (void) +{ + GtkTreeRBTree *retval; + + retval = g_new (GtkTreeRBTree, 1); + retval->parent_tree = NULL; + retval->parent_node = NULL; + + retval->root = (GtkTreeRBNode *) &nil; + + return retval; +} + +static void +gtk_tree_rbtree_free_helper (GtkTreeRBTree *tree, + GtkTreeRBNode *node, + gpointer data) +{ + if (node->children) + gtk_tree_rbtree_free (node->children); + + gtk_tree_rbnode_free (node); +} + +void +gtk_tree_rbtree_free (GtkTreeRBTree *tree) +{ + gtk_tree_rbtree_traverse (tree, + tree->root, + G_POST_ORDER, + gtk_tree_rbtree_free_helper, + NULL); + + if (tree->parent_node && + tree->parent_node->children == tree) + tree->parent_node->children = NULL; + g_free (tree); +} + +static void +gtk_rbnode_adjust (GtkTreeRBTree *tree, + GtkTreeRBNode *node, + int count_diff, + int total_count_diff, + int offset_diff) +{ + while (tree && node && !gtk_tree_rbtree_is_nil (node)) + { + fixup_validation (tree, node); + node->offset += offset_diff; + node->count += count_diff; + node->total_count += total_count_diff; + + node = node->parent; + if (gtk_tree_rbtree_is_nil (node)) + { + node = tree->parent_node; + tree = tree->parent_tree; + count_diff = 0; + } + } +} + +void +gtk_tree_rbtree_remove (GtkTreeRBTree *tree) +{ +#ifdef G_ENABLE_DEBUG + GtkTreeRBTree *tmp_tree; + + if (GTK_DEBUG_CHECK (TREE)) + gtk_tree_rbtree_test (G_STRLOC, tree); +#endif + + /* ugly hack to make fixup_validation work in the first iteration of the + * loop below */ + GTK_TREE_RBNODE_UNSET_FLAG (tree->root, GTK_TREE_RBNODE_DESCENDANTS_INVALID); + + gtk_rbnode_adjust (tree->parent_tree, + tree->parent_node, + 0, + -(int) tree->root->total_count, + -tree->root->offset); + +#ifdef G_ENABLE_DEBUG + tmp_tree = tree->parent_tree; +#endif + + gtk_tree_rbtree_free (tree); + +#ifdef G_ENABLE_DEBUG + if (GTK_DEBUG_CHECK (TREE)) + gtk_tree_rbtree_test (G_STRLOC, tmp_tree); +#endif +} + + +GtkTreeRBNode * +gtk_tree_rbtree_insert_after (GtkTreeRBTree *tree, + GtkTreeRBNode *current, + int height, + gboolean valid) +{ + GtkTreeRBNode *node; + gboolean right = TRUE; + +#ifdef G_ENABLE_DEBUG + if (GTK_DEBUG_CHECK (TREE)) + { + GString *s; + + s = g_string_new (""); + g_string_append_printf (s, "gtk_tree_rbtree_insert_after: %p\n", current); + gtk_tree_rbtree_debug_spew (tree, s); + g_message ("%s", s->str); + g_string_free (s, TRUE); + gtk_tree_rbtree_test (G_STRLOC, tree); + } +#endif + + if (current != NULL && !gtk_tree_rbtree_is_nil (current->right)) + { + current = current->right; + while (!gtk_tree_rbtree_is_nil (current->left)) + current = current->left; + right = FALSE; + } + /* setup new node */ + node = gtk_tree_rbnode_new (tree, height); + + /* insert node in tree */ + if (current) + { + node->parent = current; + if (right) + current->right = node; + else + current->left = node; + gtk_rbnode_adjust (tree, node->parent, + 1, 1, height); + } + else + { + g_assert (gtk_tree_rbtree_is_nil (tree->root)); + tree->root = node; + gtk_rbnode_adjust (tree->parent_tree, tree->parent_node, + 0, 1, height); + } + + if (valid) + gtk_tree_rbtree_node_mark_valid (tree, node); + else + gtk_tree_rbtree_node_mark_invalid (tree, node); + + gtk_tree_rbtree_insert_fixup (tree, node); + +#ifdef G_ENABLE_DEBUG + if (GTK_DEBUG_CHECK (TREE)) + { + GString *s; + + s = g_string_new ("gtk_tree_rbtree_insert_after finished...\n"); + gtk_tree_rbtree_debug_spew (tree, s); + g_message ("%s", s->str); + g_string_free (s, TRUE); + gtk_tree_rbtree_test (G_STRLOC, tree); + } +#endif + + return node; +} + +GtkTreeRBNode * +gtk_tree_rbtree_insert_before (GtkTreeRBTree *tree, + GtkTreeRBNode *current, + int height, + gboolean valid) +{ + GtkTreeRBNode *node; + gboolean left = TRUE; + +#ifdef G_ENABLE_DEBUG + if (GTK_DEBUG_CHECK (TREE)) + { + GString *s; + + s = g_string_new (""); + g_string_append_printf (s, "gtk_tree_rbtree_insert_before: %p\n", current); + gtk_tree_rbtree_debug_spew (tree, s); + g_message ("%s", s->str); + g_string_free (s, TRUE); + gtk_tree_rbtree_test (G_STRLOC, tree); + } +#endif + + if (current != NULL && !gtk_tree_rbtree_is_nil (current->left)) + { + current = current->left; + while (!gtk_tree_rbtree_is_nil (current->right)) + current = current->right; + left = FALSE; + } + + /* setup new node */ + node = gtk_tree_rbnode_new (tree, height); + + /* insert node in tree */ + if (current) + { + node->parent = current; + if (left) + current->left = node; + else + current->right = node; + gtk_rbnode_adjust (tree, node->parent, + 1, 1, height); + } + else + { + g_assert (gtk_tree_rbtree_is_nil (tree->root)); + tree->root = node; + gtk_rbnode_adjust (tree->parent_tree, tree->parent_node, + 0, 1, height); + } + + if (valid) + gtk_tree_rbtree_node_mark_valid (tree, node); + else + gtk_tree_rbtree_node_mark_invalid (tree, node); + + gtk_tree_rbtree_insert_fixup (tree, node); + +#ifdef G_ENABLE_DEBUG + if (GTK_DEBUG_CHECK (TREE)) + { + GString *s; + + s = g_string_new ("gtk_tree_rbtree_insert_before finished...\n"); + gtk_tree_rbtree_debug_spew (tree, s); + g_message ("%s", s->str); + g_string_free (s, TRUE); + gtk_tree_rbtree_test (G_STRLOC, tree); + } +#endif + + return node; +} + +GtkTreeRBNode * +gtk_tree_rbtree_find_count (GtkTreeRBTree *tree, + int count) +{ + GtkTreeRBNode *node; + + node = tree->root; + while (!gtk_tree_rbtree_is_nil (node) && (node->left->count + 1 != count)) + { + if (node->left->count >= count) + node = node->left; + else + { + count -= (node->left->count + 1); + node = node->right; + } + } + if (gtk_tree_rbtree_is_nil (node)) + return NULL; + return node; +} + +void +gtk_tree_rbtree_node_set_height (GtkTreeRBTree *tree, + GtkTreeRBNode *node, + int height) +{ + int diff = height - GTK_TREE_RBNODE_GET_HEIGHT (node); + + if (diff == 0) + return; + + gtk_rbnode_adjust (tree, node, 0, 0, diff); + +#ifdef G_ENABLE_DEBUG + if (GTK_DEBUG_CHECK (TREE)) + gtk_tree_rbtree_test (G_STRLOC, tree); +#endif +} + +void +gtk_tree_rbtree_node_mark_invalid (GtkTreeRBTree *tree, + GtkTreeRBNode *node) +{ + if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_INVALID)) + return; + + GTK_TREE_RBNODE_SET_FLAG (node, GTK_TREE_RBNODE_INVALID); + do + { + if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_DESCENDANTS_INVALID)) + return; + GTK_TREE_RBNODE_SET_FLAG (node, GTK_TREE_RBNODE_DESCENDANTS_INVALID); + node = node->parent; + if (gtk_tree_rbtree_is_nil (node)) + { + node = tree->parent_node; + tree = tree->parent_tree; + } + } + while (node); +} + +#if 0 +/* Draconian version. */ +void +gtk_tree_rbtree_node_mark_invalid (GtkTreeRBTree *tree, + GtkTreeRBNode *node) +{ + GTK_TREE_RBNODE_SET_FLAG (node, GTK_TREE_RBNODE_INVALID); + do + { + fixup_validation (tree, node); + node = node->parent; + if (gtk_tree_rbtree_is_nil (node)) + { + node = tree->parent_node; + tree = tree->parent_tree; + } + } + while (node); +} +#endif + +void +gtk_tree_rbtree_node_mark_valid (GtkTreeRBTree *tree, + GtkTreeRBNode *node) +{ + if ((!GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_INVALID)) && + (!GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_COLUMN_INVALID))) + return; + + GTK_TREE_RBNODE_UNSET_FLAG (node, GTK_TREE_RBNODE_INVALID); + GTK_TREE_RBNODE_UNSET_FLAG (node, GTK_TREE_RBNODE_COLUMN_INVALID); + + do + { + if ((GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_INVALID)) || + (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_COLUMN_INVALID)) || + (node->children && GTK_TREE_RBNODE_FLAG_SET (node->children->root, GTK_TREE_RBNODE_DESCENDANTS_INVALID)) || + (GTK_TREE_RBNODE_FLAG_SET (node->left, GTK_TREE_RBNODE_DESCENDANTS_INVALID)) || + (GTK_TREE_RBNODE_FLAG_SET (node->right, GTK_TREE_RBNODE_DESCENDANTS_INVALID))) + return; + + GTK_TREE_RBNODE_UNSET_FLAG (node, GTK_TREE_RBNODE_DESCENDANTS_INVALID); + node = node->parent; + if (gtk_tree_rbtree_is_nil (node)) + { + node = tree->parent_node; + tree = tree->parent_tree; + } + } + while (node); +} + +#if 0 +/* Draconian version */ +void +gtk_tree_rbtree_node_mark_valid (GtkTreeRBTree *tree, + GtkTreeRBNode *node) +{ + GTK_TREE_RBNODE_UNSET_FLAG (node, GTK_TREE_RBNODE_INVALID); + GTK_TREE_RBNODE_UNSET_FLAG (node, GTK_TREE_RBNODE_COLUMN_INVALID); + + do + { + fixup_validation (tree, node); + node = node->parent; + if (gtk_tree_rbtree_is_nil (node)) + { + node = tree->parent_node; + tree = tree->parent_tree; + } + } + while (node); +} +#endif +/* Assume tree is the root node as it doesn't set DESCENDANTS_INVALID above. + */ +void +gtk_tree_rbtree_column_invalid (GtkTreeRBTree *tree) +{ + GtkTreeRBNode *node; + + if (tree == NULL) + return; + + for (node = gtk_tree_rbtree_first (tree); + node != NULL; + node = gtk_tree_rbtree_next (tree, node)) + { + if (!(GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_INVALID))) + GTK_TREE_RBNODE_SET_FLAG (node, GTK_TREE_RBNODE_COLUMN_INVALID); + GTK_TREE_RBNODE_SET_FLAG (node, GTK_TREE_RBNODE_DESCENDANTS_INVALID); + + if (node->children) + gtk_tree_rbtree_column_invalid (node->children); + } +} + +void +gtk_tree_rbtree_mark_invalid (GtkTreeRBTree *tree) +{ + GtkTreeRBNode *node; + + if (tree == NULL) + return; + + for (node = gtk_tree_rbtree_first (tree); + node != NULL; + node = gtk_tree_rbtree_next (tree, node)) + { + GTK_TREE_RBNODE_SET_FLAG (node, GTK_TREE_RBNODE_INVALID); + GTK_TREE_RBNODE_SET_FLAG (node, GTK_TREE_RBNODE_DESCENDANTS_INVALID); + + if (node->children) + gtk_tree_rbtree_mark_invalid (node->children); + } +} + +void +gtk_tree_rbtree_set_fixed_height (GtkTreeRBTree *tree, + int height, + gboolean mark_valid) +{ + GtkTreeRBNode *node; + + if (tree == NULL) + return; + + for (node = gtk_tree_rbtree_first (tree); + node != NULL; + node = gtk_tree_rbtree_next (tree, node)) + { + if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_INVALID)) + { + gtk_tree_rbtree_node_set_height (tree, node, height); + if (mark_valid) + gtk_tree_rbtree_node_mark_valid (tree, node); + } + + if (node->children) + gtk_tree_rbtree_set_fixed_height (node->children, height, mark_valid); + } +} + +static void +reorder_prepare (GtkTreeRBTree *tree, + GtkTreeRBNode *node, + gpointer data) +{ + node->offset -= node->left->offset + node->right->offset; + GTK_TREE_RBNODE_UNSET_FLAG (node, GTK_TREE_RBNODE_DESCENDANTS_INVALID); +} + +static void +reorder_fixup (GtkTreeRBTree *tree, + GtkTreeRBNode *node, + gpointer data) +{ + node->offset += node->left->offset + node->right->offset; + node->count = 1 + node->left->count + node->right->count; + fixup_validation (tree, node); + fixup_total_count (tree, node); +} + +static void +reorder_copy_node (GtkTreeRBTree *tree, + GtkTreeRBNode *to, + GtkTreeRBNode *from) +{ + to->flags = (to->flags & GTK_TREE_RBNODE_NON_COLORS) | GTK_TREE_RBNODE_GET_COLOR (from); + + to->left = from->left; + if (!gtk_tree_rbtree_is_nil (to->left)) + to->left->parent = to; + + to->right = from->right; + if (!gtk_tree_rbtree_is_nil (to->right)) + to->right->parent = to; + + to->parent = from->parent; + if (gtk_tree_rbtree_is_nil (to->parent)) + tree->root = to; + else if (to->parent->left == from) + to->parent->left = to; + else if (to->parent->right == from) + to->parent->right = to; +} + +/* It basically pulls everything out of the tree, rearranges it, and puts it + * back together. Our strategy is to keep the old RBTree intact, and just + * rearrange the contents. When that is done, we go through and update the + * heights. There is probably a more elegant way to write this function. If + * anyone wants to spend the time writing it, patches will be accepted. + */ +void +gtk_tree_rbtree_reorder (GtkTreeRBTree *tree, + int *new_order, + int length) +{ + GtkTreeRBNode **nodes; + GtkTreeRBNode *node; + int i, j; + + g_return_if_fail (tree != NULL); + g_return_if_fail (length > 0); + g_return_if_fail (tree->root->count == length); + + nodes = g_new (GtkTreeRBNode *, length); + + gtk_tree_rbtree_traverse (tree, tree->root, G_PRE_ORDER, reorder_prepare, NULL); + + for (node = gtk_tree_rbtree_first (tree), i = 0; + node; + node = gtk_tree_rbtree_next (tree, node), i++) + { + nodes[i] = node; + } + + for (i = 0; i < length; i++) + { + GtkTreeRBNode tmp = { 0, }; + GSList *l, *cycle = NULL; + + tmp.offset = -1; + + /* already swapped */ + if (nodes[i] == NULL) + continue; + /* no need to swap */ + if (new_order[i] == i) + continue; + + /* make a list out of the pending nodes */ + for (j = i; new_order[j] != i; j = new_order[j]) + { + cycle = g_slist_prepend (cycle, nodes[j]); + nodes[j] = NULL; + } + + node = nodes[j]; + reorder_copy_node (tree, &tmp, node); + for (l = cycle; l; l = l->next) + { + reorder_copy_node (tree, node, l->data); + node = l->data; + } + + reorder_copy_node (tree, node, &tmp); + nodes[j] = NULL; + g_slist_free (cycle); + } + + gtk_tree_rbtree_traverse (tree, tree->root, G_POST_ORDER, reorder_fixup, NULL); + + g_free (nodes); +} + +/** + * gtk_tree_rbtree_contains: + * @tree: a tree + * @potential_child: a potential child of @tree + * + * Checks if @potential_child is a child (direct or via intermediate + * trees) of @tree. + * + * Returns: %TRUE if @potential_child is a child of @tree. + **/ +gboolean +gtk_tree_rbtree_contains (GtkTreeRBTree *tree, + GtkTreeRBTree *potential_child) +{ + g_return_val_if_fail (tree != NULL, FALSE); + g_return_val_if_fail (potential_child != NULL, FALSE); + + do + { + potential_child = potential_child->parent_tree; + if (potential_child == tree) + return TRUE; + } + while (potential_child != NULL); + + return FALSE; +} + +int +gtk_tree_rbtree_node_find_offset (GtkTreeRBTree *tree, + GtkTreeRBNode *node) +{ + GtkTreeRBNode *last; + int retval; + + g_assert (node); + g_assert (node->left); + + retval = node->left->offset; + + while (tree && node && !gtk_tree_rbtree_is_nil (node)) + { + last = node; + node = node->parent; + + /* Add left branch, plus children, iff we came from the right */ + if (node->right == last) + retval += node->offset - node->right->offset; + + if (gtk_tree_rbtree_is_nil (node)) + { + node = tree->parent_node; + tree = tree->parent_tree; + + /* Add the parent node, plus the left branch. */ + if (node) + retval += node->left->offset + GTK_TREE_RBNODE_GET_HEIGHT (node); + } + } + return retval; +} + +guint +gtk_tree_rbtree_node_get_index (GtkTreeRBTree *tree, + GtkTreeRBNode *node) +{ + GtkTreeRBNode *last; + guint retval; + + g_assert (node); + g_assert (node->left); + + retval = node->left->total_count; + + while (tree && node && !gtk_tree_rbtree_is_nil (node)) + { + last = node; + node = node->parent; + + /* Add left branch, plus children, iff we came from the right */ + if (node->right == last) + retval += node->total_count - node->right->total_count; + + if (gtk_tree_rbtree_is_nil (node)) + { + node = tree->parent_node; + tree = tree->parent_tree; + + /* Add the parent node, plus the left branch. */ + if (node) + retval += node->left->total_count + 1; /* 1 == GTK_TREE_RBNODE_GET_PARITY() */ + } + } + + return retval; +} + +static int +gtk_rbtree_real_find_offset (GtkTreeRBTree *tree, + int height, + GtkTreeRBTree **new_tree, + GtkTreeRBNode **new_node) +{ + GtkTreeRBNode *tmp_node; + + g_assert (tree); + + if (height < 0) + { + *new_tree = NULL; + *new_node = NULL; + + return 0; + } + + + tmp_node = tree->root; + while (!gtk_tree_rbtree_is_nil (tmp_node) && + (tmp_node->left->offset > height || + (tmp_node->offset - tmp_node->right->offset) < height)) + { + if (tmp_node->left->offset > height) + tmp_node = tmp_node->left; + else + { + height -= (tmp_node->offset - tmp_node->right->offset); + tmp_node = tmp_node->right; + } + } + if (gtk_tree_rbtree_is_nil (tmp_node)) + { + *new_tree = NULL; + *new_node = NULL; + return 0; + } + if (tmp_node->children) + { + if ((tmp_node->offset - + tmp_node->right->offset - + tmp_node->children->root->offset) > height) + { + *new_tree = tree; + *new_node = tmp_node; + return (height - tmp_node->left->offset); + } + return gtk_rbtree_real_find_offset (tmp_node->children, + height - tmp_node->left->offset - + (tmp_node->offset - + tmp_node->left->offset - + tmp_node->right->offset - + tmp_node->children->root->offset), + new_tree, + new_node); + } + *new_tree = tree; + *new_node = tmp_node; + return (height - tmp_node->left->offset); +} + +int +gtk_tree_rbtree_find_offset (GtkTreeRBTree *tree, + int height, + GtkTreeRBTree **new_tree, + GtkTreeRBNode **new_node) +{ + g_assert (tree); + + if ((height < 0) || + (height >= tree->root->offset)) + { + *new_tree = NULL; + *new_node = NULL; + + return 0; + } + return gtk_rbtree_real_find_offset (tree, height, new_tree, new_node); +} + +gboolean +gtk_tree_rbtree_find_index (GtkTreeRBTree *tree, + guint index, + GtkTreeRBTree **new_tree, + GtkTreeRBNode **new_node) +{ + GtkTreeRBNode *tmp_node; + + g_assert (tree); + + tmp_node = tree->root; + while (!gtk_tree_rbtree_is_nil (tmp_node)) + { + if (tmp_node->left->total_count > index) + { + tmp_node = tmp_node->left; + } + else if (tmp_node->total_count - tmp_node->right->total_count <= index) + { + index -= tmp_node->total_count - tmp_node->right->total_count; + tmp_node = tmp_node->right; + } + else + { + index -= tmp_node->left->total_count; + break; + } + } + if (gtk_tree_rbtree_is_nil (tmp_node)) + { + *new_tree = NULL; + *new_node = NULL; + return FALSE; + } + + if (index > 0) + { + g_assert (tmp_node->children); + + return gtk_tree_rbtree_find_index (tmp_node->children, + index - 1, + new_tree, + new_node); + } + + *new_tree = tree; + *new_node = tmp_node; + return TRUE; +} + +void +gtk_tree_rbtree_remove_node (GtkTreeRBTree *tree, + GtkTreeRBNode *node) +{ + GtkTreeRBNode *x, *y; + int y_height; + guint y_total_count; + + g_return_if_fail (tree != NULL); + g_return_if_fail (node != NULL); + + +#ifdef G_ENABLE_DEBUG + if (GTK_DEBUG_CHECK (TREE)) + { + GString *s; + + s = g_string_new (""); + g_string_append_printf (s, "gtk_tree_rbtree_remove_node: %p\n", node); + gtk_tree_rbtree_debug_spew (tree, s); + g_message ("%s", s->str); + g_string_free (s, TRUE); + gtk_tree_rbtree_test (G_STRLOC, tree); + } +#endif + + /* make sure we're deleting a node that's actually in the tree */ + for (x = node; !gtk_tree_rbtree_is_nil (x->parent); x = x->parent) + ; + g_return_if_fail (x == tree->root); + +#ifdef G_ENABLE_DEBUG + if (GTK_DEBUG_CHECK (TREE)) + gtk_tree_rbtree_test (G_STRLOC, tree); +#endif + + if (gtk_tree_rbtree_is_nil (node->left) || + gtk_tree_rbtree_is_nil (node->right)) + { + y = node; + } + else + { + y = node->right; + + while (!gtk_tree_rbtree_is_nil (y->left)) + y = y->left; + } + + y_height = GTK_TREE_RBNODE_GET_HEIGHT (y) + + (y->children ? y->children->root->offset : 0); + y_total_count = 1 + (y->children ? y->children->root->total_count : 0); + + /* x is y's only child, or nil */ + if (!gtk_tree_rbtree_is_nil (y->left)) + x = y->left; + else + x = y->right; + + /* remove y from the parent chain */ + if (!gtk_tree_rbtree_is_nil (x)) + x->parent = y->parent; + if (!gtk_tree_rbtree_is_nil (y->parent)) + { + if (y == y->parent->left) + y->parent->left = x; + else + y->parent->right = x; + } + else + { + tree->root = x; + } + + /* We need to clean up the validity of the tree. + */ + gtk_rbnode_adjust (tree, y, -1, -y_total_count, -y_height); + + if (GTK_TREE_RBNODE_GET_COLOR (y) == GTK_TREE_RBNODE_BLACK) + gtk_tree_rbtree_remove_node_fixup (tree, x, y->parent); + + if (y != node) + { + int node_height, node_total_count; + + /* We want to see how much we remove from the aggregate values. + * This is all the children we remove plus the node's values. + */ + node_height = GTK_TREE_RBNODE_GET_HEIGHT (node) + + (node->children ? node->children->root->offset : 0); + node_total_count = 1 + + (node->children ? node->children->root->total_count : 0); + + /* Move the node over */ + if (GTK_TREE_RBNODE_GET_COLOR (node) != GTK_TREE_RBNODE_GET_COLOR (y)) + y->flags ^= (GTK_TREE_RBNODE_BLACK | GTK_TREE_RBNODE_RED); + + y->left = node->left; + if (!gtk_tree_rbtree_is_nil (y->left)) + y->left->parent = y; + y->right = node->right; + if (!gtk_tree_rbtree_is_nil (y->right)) + y->right->parent = y; + y->parent = node->parent; + if (!gtk_tree_rbtree_is_nil (y->parent)) + { + if (y->parent->left == node) + y->parent->left = y; + else + y->parent->right = y; + } + else + { + tree->root = y; + } + y->count = node->count; + y->total_count = node->total_count; + y->offset = node->offset; + + gtk_rbnode_adjust (tree, y, + 0, + y_total_count - node_total_count, + y_height - node_height); + } + + gtk_tree_rbnode_free (node); + +#ifdef G_ENABLE_DEBUG + if (GTK_DEBUG_CHECK (TREE)) + { + GString *s; + + s = g_string_new ("gtk_tree_rbtree_remove_node finished...\n"); + gtk_tree_rbtree_debug_spew (tree, s); + g_message ("%s", s->str); + g_string_free (s, TRUE); + gtk_tree_rbtree_test (G_STRLOC, tree); + } +#endif +} + +GtkTreeRBNode * +gtk_tree_rbtree_first (GtkTreeRBTree *tree) +{ + GtkTreeRBNode *node; + + node = tree->root; + + if (gtk_tree_rbtree_is_nil (node)) + return NULL; + + while (!gtk_tree_rbtree_is_nil (node->left)) + node = node->left; + + return node; +} + +GtkTreeRBNode * +gtk_tree_rbtree_next (GtkTreeRBTree *tree, + GtkTreeRBNode *node) +{ + g_return_val_if_fail (tree != NULL, NULL); + g_return_val_if_fail (node != NULL, NULL); + + /* Case 1: the node's below us. */ + if (!gtk_tree_rbtree_is_nil (node->right)) + { + node = node->right; + while (!gtk_tree_rbtree_is_nil (node->left)) + node = node->left; + return node; + } + + /* Case 2: it's an ancestor */ + while (!gtk_tree_rbtree_is_nil (node->parent)) + { + if (node->parent->right == node) + node = node->parent; + else + return (node->parent); + } + + /* Case 3: There is no next node */ + return NULL; +} + +GtkTreeRBNode * +gtk_tree_rbtree_prev (GtkTreeRBTree *tree, + GtkTreeRBNode *node) +{ + g_return_val_if_fail (tree != NULL, NULL); + g_return_val_if_fail (node != NULL, NULL); + + /* Case 1: the node's below us. */ + if (!gtk_tree_rbtree_is_nil (node->left)) + { + node = node->left; + while (!gtk_tree_rbtree_is_nil (node->right)) + node = node->right; + return node; + } + + /* Case 2: it's an ancestor */ + while (!gtk_tree_rbtree_is_nil (node->parent)) + { + if (node->parent->left == node) + node = node->parent; + else + return (node->parent); + } + + /* Case 3: There is no next node */ + return NULL; +} + +void +gtk_tree_rbtree_next_full (GtkTreeRBTree *tree, + GtkTreeRBNode *node, + GtkTreeRBTree **new_tree, + GtkTreeRBNode **new_node) +{ + g_return_if_fail (tree != NULL); + g_return_if_fail (node != NULL); + g_return_if_fail (new_tree != NULL); + g_return_if_fail (new_node != NULL); + + if (node->children) + { + *new_tree = node->children; + *new_node = (*new_tree)->root; + while (!gtk_tree_rbtree_is_nil ((*new_node)->left)) + *new_node = (*new_node)->left; + return; + } + + *new_tree = tree; + *new_node = gtk_tree_rbtree_next (tree, node); + + while ((*new_node == NULL) && + (*new_tree != NULL)) + { + *new_node = (*new_tree)->parent_node; + *new_tree = (*new_tree)->parent_tree; + if (*new_tree) + *new_node = gtk_tree_rbtree_next (*new_tree, *new_node); + } +} + +void +gtk_tree_rbtree_prev_full (GtkTreeRBTree *tree, + GtkTreeRBNode *node, + GtkTreeRBTree **new_tree, + GtkTreeRBNode **new_node) +{ + g_return_if_fail (tree != NULL); + g_return_if_fail (node != NULL); + g_return_if_fail (new_tree != NULL); + g_return_if_fail (new_node != NULL); + + *new_tree = tree; + *new_node = gtk_tree_rbtree_prev (tree, node); + + if (*new_node == NULL) + { + *new_node = (*new_tree)->parent_node; + *new_tree = (*new_tree)->parent_tree; + } + else + { + while ((*new_node)->children) + { + *new_tree = (*new_node)->children; + *new_node = (*new_tree)->root; + while (!gtk_tree_rbtree_is_nil ((*new_node)->right)) + *new_node = (*new_node)->right; + } + } +} + +int +gtk_tree_rbtree_get_depth (GtkTreeRBTree *tree) +{ + GtkTreeRBTree *tmp_tree; + int depth = 0; + + tmp_tree = tree->parent_tree; + while (tmp_tree) + { + ++depth; + tmp_tree = tmp_tree->parent_tree; + } + + return depth; +} + +static void +gtk_tree_rbtree_traverse_pre_order (GtkTreeRBTree *tree, + GtkTreeRBNode *node, + GtkTreeRBTreeTraverseFunc func, + gpointer data) +{ + if (gtk_tree_rbtree_is_nil (node)) + return; + + (*func)(tree, node, data); + gtk_tree_rbtree_traverse_pre_order (tree, node->left, func, data); + gtk_tree_rbtree_traverse_pre_order (tree, node->right, func, data); +} + +static void +gtk_tree_rbtree_traverse_post_order (GtkTreeRBTree *tree, + GtkTreeRBNode *node, + GtkTreeRBTreeTraverseFunc func, + gpointer data) +{ + if (gtk_tree_rbtree_is_nil (node)) + return; + + gtk_tree_rbtree_traverse_post_order (tree, node->left, func, data); + gtk_tree_rbtree_traverse_post_order (tree, node->right, func, data); + (*func)(tree, node, data); +} + +void +gtk_tree_rbtree_traverse (GtkTreeRBTree *tree, + GtkTreeRBNode *node, + GTraverseType order, + GtkTreeRBTreeTraverseFunc func, + gpointer data) +{ + g_return_if_fail (tree != NULL); + g_return_if_fail (node != NULL); + g_return_if_fail (func != NULL); + g_return_if_fail (order <= G_LEVEL_ORDER); + + switch (order) + { + case G_PRE_ORDER: + gtk_tree_rbtree_traverse_pre_order (tree, node, func, data); + break; + + case G_POST_ORDER: + gtk_tree_rbtree_traverse_post_order (tree, node, func, data); + break; + + case G_IN_ORDER: + case G_LEVEL_ORDER: + default: + g_warning ("unsupported traversal order."); + break; + } +} + +static inline +void fixup_validation (GtkTreeRBTree *tree, + GtkTreeRBNode *node) +{ + if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_INVALID) || + GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_COLUMN_INVALID) || + GTK_TREE_RBNODE_FLAG_SET (node->left, GTK_TREE_RBNODE_DESCENDANTS_INVALID) || + GTK_TREE_RBNODE_FLAG_SET (node->right, GTK_TREE_RBNODE_DESCENDANTS_INVALID) || + (node->children != NULL && GTK_TREE_RBNODE_FLAG_SET (node->children->root, GTK_TREE_RBNODE_DESCENDANTS_INVALID))) + { + GTK_TREE_RBNODE_SET_FLAG (node, GTK_TREE_RBNODE_DESCENDANTS_INVALID); + } + else + { + GTK_TREE_RBNODE_UNSET_FLAG (node, GTK_TREE_RBNODE_DESCENDANTS_INVALID); + } +} + +static inline +void fixup_total_count (GtkTreeRBTree *tree, + GtkTreeRBNode *node) +{ + node->total_count = 1 + + (node->children != NULL ? node->children->root->total_count : 0) + + node->left->total_count + node->right->total_count; +} + +#ifdef G_ENABLE_DEBUG +static guint +get_total_count (GtkTreeRBNode *node) +{ + guint child_total = 0; + + child_total += (guint) node->left->total_count; + child_total += (guint) node->right->total_count; + + if (node->children) + child_total += (guint) node->children->root->total_count; + + return child_total + 1; +} + +static guint +count_total (GtkTreeRBTree *tree, + GtkTreeRBNode *node) +{ + guint res; + + if (gtk_tree_rbtree_is_nil (node)) + return 0; + + res = + count_total (tree, node->left) + + count_total (tree, node->right) + + (guint) 1 + + (node->children ? count_total (node->children, node->children->root) : 0); + + if (res != node->total_count) + g_error ("total count incorrect for node"); + + if (get_total_count (node) != node->total_count) + g_error ("Node has incorrect total count %u, should be %u", node->total_count, get_total_count (node)); + + return res; +} + +static int +_count_nodes (GtkTreeRBTree *tree, + GtkTreeRBNode *node) +{ + int res; + if (gtk_tree_rbtree_is_nil (node)) + return 0; + + g_assert (node->left); + g_assert (node->right); + + res = (_count_nodes (tree, node->left) + + _count_nodes (tree, node->right) + 1); + + if (res != node->count) + g_error ("Tree failed"); + return res; +} + +static void +gtk_tree_rbtree_test_height (GtkTreeRBTree *tree, + GtkTreeRBNode *node) +{ + int computed_offset = 0; + + /* This whole test is sort of a useless truism. */ + + if (!gtk_tree_rbtree_is_nil (node->left)) + computed_offset += node->left->offset; + + if (!gtk_tree_rbtree_is_nil (node->right)) + computed_offset += node->right->offset; + + if (node->children && !gtk_tree_rbtree_is_nil (node->children->root)) + computed_offset += node->children->root->offset; + + if (GTK_TREE_RBNODE_GET_HEIGHT (node) + computed_offset != node->offset) + g_error ("node has broken offset"); + + if (!gtk_tree_rbtree_is_nil (node->left)) + gtk_tree_rbtree_test_height (tree, node->left); + + if (!gtk_tree_rbtree_is_nil (node->right)) + gtk_tree_rbtree_test_height (tree, node->right); + + if (node->children && !gtk_tree_rbtree_is_nil (node->children->root)) + gtk_tree_rbtree_test_height (node->children, node->children->root); +} + +static void +gtk_tree_rbtree_test_dirty (GtkTreeRBTree *tree, + GtkTreeRBNode *node, + int expected_dirtyness) +{ + g_assert (node); + + if (expected_dirtyness) + { + g_assert (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_COLUMN_INVALID) || + GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_INVALID) || + GTK_TREE_RBNODE_FLAG_SET (node->left, GTK_TREE_RBNODE_DESCENDANTS_INVALID) || + GTK_TREE_RBNODE_FLAG_SET (node->right, GTK_TREE_RBNODE_DESCENDANTS_INVALID) || + (node->children && GTK_TREE_RBNODE_FLAG_SET (node->children->root, GTK_TREE_RBNODE_DESCENDANTS_INVALID))); + } + else + { + g_assert (!GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_COLUMN_INVALID) && + !GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_INVALID)); + if (!gtk_tree_rbtree_is_nil (node->left)) + g_assert (!GTK_TREE_RBNODE_FLAG_SET (node->left, GTK_TREE_RBNODE_DESCENDANTS_INVALID)); + if (!gtk_tree_rbtree_is_nil (node->right)) + g_assert (!GTK_TREE_RBNODE_FLAG_SET (node->right, GTK_TREE_RBNODE_DESCENDANTS_INVALID)); + if (node->children != NULL) + g_assert (!GTK_TREE_RBNODE_FLAG_SET (node->children->root, GTK_TREE_RBNODE_DESCENDANTS_INVALID)); + } + + if (!gtk_tree_rbtree_is_nil (node->left)) + gtk_tree_rbtree_test_dirty (tree, node->left, GTK_TREE_RBNODE_FLAG_SET (node->left, GTK_TREE_RBNODE_DESCENDANTS_INVALID)); + if (!gtk_tree_rbtree_is_nil (node->right)) + gtk_tree_rbtree_test_dirty (tree, node->right, GTK_TREE_RBNODE_FLAG_SET (node->right, GTK_TREE_RBNODE_DESCENDANTS_INVALID)); + if (node->children != NULL && !gtk_tree_rbtree_is_nil (node->children->root)) + gtk_tree_rbtree_test_dirty (node->children, node->children->root, GTK_TREE_RBNODE_FLAG_SET (node->children->root, GTK_TREE_RBNODE_DESCENDANTS_INVALID)); +} + +static void gtk_tree_rbtree_test_structure (GtkTreeRBTree *tree); + +static void +gtk_tree_rbtree_test_structure_helper (GtkTreeRBTree *tree, + GtkTreeRBNode *node) +{ + g_assert (!gtk_tree_rbtree_is_nil (node)); + + g_assert (node->left != NULL); + g_assert (node->right != NULL); + g_assert (node->parent != NULL); + + if (!gtk_tree_rbtree_is_nil (node->left)) + { + g_assert (node->left->parent == node); + gtk_tree_rbtree_test_structure_helper (tree, node->left); + } + if (!gtk_tree_rbtree_is_nil (node->right)) + { + g_assert (node->right->parent == node); + gtk_tree_rbtree_test_structure_helper (tree, node->right); + } + + if (node->children != NULL) + { + g_assert (node->children->parent_tree == tree); + g_assert (node->children->parent_node == node); + + gtk_tree_rbtree_test_structure (node->children); + } +} +static void +gtk_tree_rbtree_test_structure (GtkTreeRBTree *tree) +{ + g_assert (tree->root); + if (gtk_tree_rbtree_is_nil (tree->root)) + return; + + g_assert (gtk_tree_rbtree_is_nil (tree->root->parent)); + gtk_tree_rbtree_test_structure_helper (tree, tree->root); +} + +static void +gtk_tree_rbtree_test (const char *where, + GtkTreeRBTree *tree) +{ + GtkTreeRBTree *tmp_tree; + + if (tree == NULL) + return; + + /* Test the entire tree */ + tmp_tree = tree; + while (tmp_tree->parent_tree) + tmp_tree = tmp_tree->parent_tree; + + if (gtk_tree_rbtree_is_nil (tmp_tree->root)) + return; + + gtk_tree_rbtree_test_structure (tmp_tree); + + g_assert ((_count_nodes (tmp_tree, tmp_tree->root->left) + + _count_nodes (tmp_tree, tmp_tree->root->right) + 1) == tmp_tree->root->count); + + + gtk_tree_rbtree_test_height (tmp_tree, tmp_tree->root); + gtk_tree_rbtree_test_dirty (tmp_tree, tmp_tree->root, GTK_TREE_RBNODE_FLAG_SET (tmp_tree->root, GTK_TREE_RBNODE_DESCENDANTS_INVALID)); + g_assert (count_total (tmp_tree, tmp_tree->root) == tmp_tree->root->total_count); +} + +static void +gtk_tree_rbtree_debug_spew_helper (GtkTreeRBTree *tree, + GtkTreeRBNode *node, + GString *s, + int depth) +{ + int i; + for (i = 0; i < depth; i++) + g_string_append (s, "\t"); + + g_string_append_printf (s, "(%p - %s) (Offset %d) (Parity %d) (Validity %d%d%d)\n", + node, + (GTK_TREE_RBNODE_GET_COLOR (node) == GTK_TREE_RBNODE_BLACK) ? "BLACK" : " RED ", + node->offset, + node->total_count, + (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_DESCENDANTS_INVALID)) ? 1 : 0, + (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_INVALID)) ? 1 : 0, + (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_COLUMN_INVALID)) ? 1 : 0); + if (node->children != NULL) + { + g_string_append (s, "Looking at child.\n"); + gtk_tree_rbtree_debug_spew (node->children, s); + g_string_append (s, "Done looking at child.\n"); + } + if (!gtk_tree_rbtree_is_nil (node->left)) + { + gtk_tree_rbtree_debug_spew_helper (tree, node->left, s, depth + 1); + } + if (!gtk_tree_rbtree_is_nil (node->right)) + { + gtk_tree_rbtree_debug_spew_helper (tree, node->right, s, depth + 1); + } +} + +static void +gtk_tree_rbtree_debug_spew (GtkTreeRBTree *tree, + GString *s) +{ + g_return_if_fail (tree != NULL); + + if (gtk_tree_rbtree_is_nil (tree->root)) + g_string_append (s, "Empty tree..."); + else + gtk_tree_rbtree_debug_spew_helper (tree, tree->root, s, 0); +} +#endif /* G_ENABLE_DEBUG */ diff --git a/gtk/deprecated/gtktreerbtreeprivate.h b/gtk/deprecated/gtktreerbtreeprivate.h new file mode 100644 index 0000000000..f998be27b6 --- /dev/null +++ b/gtk/deprecated/gtktreerbtreeprivate.h @@ -0,0 +1,172 @@ +/* gtkrbtreeprivate.h + * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +/* A Red-Black Tree implementation used specifically by GtkTreeView. + */ +#ifndef __GTK_TREE_RBTREE_PRIVATE_H__ +#define __GTK_TREE_RBTREE_PRIVATE_H__ + +#include + + +G_BEGIN_DECLS + + +typedef enum +{ + GTK_TREE_RBNODE_BLACK = 1 << 0, + GTK_TREE_RBNODE_RED = 1 << 1, + GTK_TREE_RBNODE_IS_PARENT = 1 << 2, + GTK_TREE_RBNODE_IS_SELECTED = 1 << 3, + GTK_TREE_RBNODE_IS_PRELIT = 1 << 4, + GTK_TREE_RBNODE_INVALID = 1 << 7, + GTK_TREE_RBNODE_COLUMN_INVALID = 1 << 8, + GTK_TREE_RBNODE_DESCENDANTS_INVALID = 1 << 9, + GTK_TREE_RBNODE_NON_COLORS = GTK_TREE_RBNODE_IS_PARENT | + GTK_TREE_RBNODE_IS_SELECTED | + GTK_TREE_RBNODE_IS_PRELIT | + GTK_TREE_RBNODE_INVALID | + GTK_TREE_RBNODE_COLUMN_INVALID | + GTK_TREE_RBNODE_DESCENDANTS_INVALID +} GtkTreeRBNodeColor; + +typedef struct _GtkTreeRBTree GtkTreeRBTree; +typedef struct _GtkTreeRBNode GtkTreeRBNode; +typedef struct _GtkTreeRBTreeView GtkTreeRBTreeView; + +typedef void (*GtkTreeRBTreeTraverseFunc) (GtkTreeRBTree *tree, + GtkTreeRBNode *node, + gpointer data); + +struct _GtkTreeRBTree +{ + GtkTreeRBNode *root; + GtkTreeRBTree *parent_tree; + GtkTreeRBNode *parent_node; +}; + +struct _GtkTreeRBNode +{ + guint flags : 14; + + /* count is the number of nodes beneath us, plus 1 for ourselves. + * i.e. node->left->count + node->right->count + 1 + */ + int count; + + GtkTreeRBNode *left; + GtkTreeRBNode *right; + GtkTreeRBNode *parent; + + /* count the number of total nodes beneath us, including nodes + * of children trees. + * i.e. node->left->count + node->right->count + node->children->root->count + 1 + */ + guint total_count; + + /* this is the total of sizes of + * node->left, node->right, our own height, and the height + * of all trees in ->children, iff children exists because + * the thing is expanded. + */ + int offset; + + /* Child trees */ + GtkTreeRBTree *children; +}; + + +#define GTK_TREE_RBNODE_GET_COLOR(node) (node?(((node->flags>K_TREE_RBNODE_RED)==GTK_TREE_RBNODE_RED)?GTK_TREE_RBNODE_RED:GTK_TREE_RBNODE_BLACK):GTK_TREE_RBNODE_BLACK) +#define GTK_TREE_RBNODE_SET_COLOR(node,color) if((node->flags&color)!=color)node->flags=node->flags^(GTK_TREE_RBNODE_RED|GTK_TREE_RBNODE_BLACK) +#define GTK_TREE_RBNODE_GET_HEIGHT(node) (node->offset-(node->left->offset+node->right->offset+(node->children?node->children->root->offset:0))) +#define GTK_TREE_RBNODE_SET_FLAG(node, flag) G_STMT_START{ (node->flags|=flag); }G_STMT_END +#define GTK_TREE_RBNODE_UNSET_FLAG(node, flag) G_STMT_START{ (node->flags&=~(flag)); }G_STMT_END +#define GTK_TREE_RBNODE_FLAG_SET(node, flag) (node?(((node->flags&flag)==flag)?TRUE:FALSE):FALSE) + + +GtkTreeRBTree * gtk_tree_rbtree_new (void); +void gtk_tree_rbtree_free (GtkTreeRBTree *tree); +void gtk_tree_rbtree_remove (GtkTreeRBTree *tree); +void gtk_tree_rbtree_destroy (GtkTreeRBTree *tree); +GtkTreeRBNode * gtk_tree_rbtree_insert_before (GtkTreeRBTree *tree, + GtkTreeRBNode *node, + int height, + gboolean valid); +GtkTreeRBNode * gtk_tree_rbtree_insert_after (GtkTreeRBTree *tree, + GtkTreeRBNode *node, + int height, + gboolean valid); +void gtk_tree_rbtree_remove_node (GtkTreeRBTree *tree, + GtkTreeRBNode *node); +gboolean gtk_tree_rbtree_is_nil (GtkTreeRBNode *node); +void gtk_tree_rbtree_reorder (GtkTreeRBTree *tree, + int *new_order, + int length); +gboolean gtk_tree_rbtree_contains (GtkTreeRBTree *tree, + GtkTreeRBTree *potential_child); +GtkTreeRBNode * gtk_tree_rbtree_find_count (GtkTreeRBTree *tree, + int count); +void gtk_tree_rbtree_node_set_height (GtkTreeRBTree *tree, + GtkTreeRBNode *node, + int height); +void gtk_tree_rbtree_node_mark_invalid (GtkTreeRBTree *tree, + GtkTreeRBNode *node); +void gtk_tree_rbtree_node_mark_valid (GtkTreeRBTree *tree, + GtkTreeRBNode *node); +void gtk_tree_rbtree_column_invalid (GtkTreeRBTree *tree); +void gtk_tree_rbtree_mark_invalid (GtkTreeRBTree *tree); +void gtk_tree_rbtree_set_fixed_height (GtkTreeRBTree *tree, + int height, + gboolean mark_valid); +int gtk_tree_rbtree_node_find_offset (GtkTreeRBTree *tree, + GtkTreeRBNode *node); +guint gtk_tree_rbtree_node_get_index (GtkTreeRBTree *tree, + GtkTreeRBNode *node); +gboolean gtk_tree_rbtree_find_index (GtkTreeRBTree *tree, + guint index, + GtkTreeRBTree **new_tree, + GtkTreeRBNode **new_node); +int gtk_tree_rbtree_find_offset (GtkTreeRBTree *tree, + int offset, + GtkTreeRBTree **new_tree, + GtkTreeRBNode **new_node); +void gtk_tree_rbtree_traverse (GtkTreeRBTree *tree, + GtkTreeRBNode *node, + GTraverseType order, + GtkTreeRBTreeTraverseFunc func, + gpointer data); +GtkTreeRBNode * gtk_tree_rbtree_first (GtkTreeRBTree *tree); +GtkTreeRBNode * gtk_tree_rbtree_next (GtkTreeRBTree *tree, + GtkTreeRBNode *node); +GtkTreeRBNode * gtk_tree_rbtree_prev (GtkTreeRBTree *tree, + GtkTreeRBNode *node); +void gtk_tree_rbtree_next_full (GtkTreeRBTree *tree, + GtkTreeRBNode *node, + GtkTreeRBTree **new_tree, + GtkTreeRBNode **new_node); +void gtk_tree_rbtree_prev_full (GtkTreeRBTree *tree, + GtkTreeRBNode *node, + GtkTreeRBTree **new_tree, + GtkTreeRBNode **new_node); + +int gtk_tree_rbtree_get_depth (GtkTreeRBTree *tree); + + +G_END_DECLS + + +#endif /* __GTK_TREE_RBTREE_PRIVATE_H__ */ diff --git a/gtk/deprecated/gtktreeselection.c b/gtk/deprecated/gtktreeselection.c new file mode 100644 index 0000000000..7043ceba9c --- /dev/null +++ b/gtk/deprecated/gtktreeselection.c @@ -0,0 +1,1615 @@ +/* gtktreeselection.h + * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#include "config.h" +#include +#include "gtktreeselection.h" +#include "gtktreeprivate.h" +#include "gtktreerbtreeprivate.h" +#include "gtkmarshalers.h" +#include "gtkprivate.h" +#include "gtktypebuiltins.h" + + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + +/** + * GtkTreeSelection: + * + * The selection object for GtkTreeView + * + * The `GtkTreeSelection` object is a helper object to manage the selection + * for a `GtkTreeView` widget. The `GtkTreeSelection` object is + * automatically created when a new `GtkTreeView` widget is created, and + * cannot exist independently of this widget. The primary reason the + * `GtkTreeSelection` objects exists is for cleanliness of code and API. + * That is, there is no conceptual reason all these functions could not be + * methods on the `GtkTreeView` widget instead of a separate function. + * + * The `GtkTreeSelection` object is gotten from a `GtkTreeView` by calling + * gtk_tree_view_get_selection(). It can be manipulated to check the + * selection status of the tree, as well as select and deselect individual + * rows. Selection is done completely view side. As a result, multiple + * views of the same model can have completely different selections. + * Additionally, you cannot change the selection of a row on the model that + * is not currently displayed by the view without expanding its parents + * first. + * + * One of the important things to remember when monitoring the selection of + * a view is that the `GtkTreeSelection`::changed signal is mostly a hint. + * That is, it may only emit one signal when a range of rows is selected. + * Additionally, it may on occasion emit a `GtkTreeSelection`::changed signal + * when nothing has happened (mostly as a result of programmers calling + * select_row on an already selected row). + */ + +typedef struct _GtkTreeSelectionClass GtkTreeSelectionClass; + +struct _GtkTreeSelection +{ + GObject parent; + + GtkTreeView *tree_view; + GtkSelectionMode type; + GtkTreeSelectionFunc user_func; + gpointer user_data; + GDestroyNotify destroy; +}; + +struct _GtkTreeSelectionClass +{ + GObjectClass parent_class; + + void (* changed) (GtkTreeSelection *selection); +}; + +static void gtk_tree_selection_finalize (GObject *object); +static int gtk_tree_selection_real_select_all (GtkTreeSelection *selection); +static int gtk_tree_selection_real_unselect_all (GtkTreeSelection *selection); +static int gtk_tree_selection_real_select_node (GtkTreeSelection *selection, + GtkTreeRBTree *tree, + GtkTreeRBNode *node, + gboolean select); +static void gtk_tree_selection_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gtk_tree_selection_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); + +enum +{ + PROP_0, + PROP_MODE, + N_PROPERTIES +}; + +enum +{ + CHANGED, + LAST_SIGNAL +}; + +static GParamSpec *properties[N_PROPERTIES]; +static guint tree_selection_signals [LAST_SIGNAL] = { 0 }; + +G_DEFINE_TYPE(GtkTreeSelection, gtk_tree_selection, G_TYPE_OBJECT) + +static void +gtk_tree_selection_class_init (GtkTreeSelectionClass *class) +{ + GObjectClass *object_class; + + object_class = (GObjectClass*) class; + + object_class->finalize = gtk_tree_selection_finalize; + object_class->set_property = gtk_tree_selection_set_property; + object_class->get_property = gtk_tree_selection_get_property; + class->changed = NULL; + + /* Properties */ + + /** + * GtkTreeSelection:mode: + * + * Selection mode. + * See gtk_tree_selection_set_mode() for more information on this property. + */ + properties[PROP_MODE] = g_param_spec_enum ("mode", NULL, NULL, + GTK_TYPE_SELECTION_MODE, + GTK_SELECTION_SINGLE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + + /* Install all properties */ + g_object_class_install_properties (object_class, N_PROPERTIES, properties); + + /* Signals */ + + /** + * GtkTreeSelection::changed: + * @treeselection: the object which received the signal. + * + * Emitted whenever the selection has (possibly) changed. Please note that + * this signal is mostly a hint. It may only be emitted once when a range + * of rows are selected, and it may occasionally be emitted when nothing + * has happened. + */ + tree_selection_signals[CHANGED] = + g_signal_new (I_("changed"), + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GtkTreeSelectionClass, changed), + NULL, NULL, + NULL, + G_TYPE_NONE, 0); +} + +static void +gtk_tree_selection_init (GtkTreeSelection *selection) +{ + selection->type = GTK_SELECTION_SINGLE; +} + +static void +gtk_tree_selection_finalize (GObject *object) +{ + GtkTreeSelection *selection = GTK_TREE_SELECTION (object); + + if (selection->destroy) + selection->destroy (selection->user_data); + + /* chain parent_class' handler */ + G_OBJECT_CLASS (gtk_tree_selection_parent_class)->finalize (object); +} + +static void +gtk_tree_selection_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + g_return_if_fail (GTK_IS_TREE_SELECTION (object)); + + switch (prop_id) + { + case PROP_MODE: + gtk_tree_selection_set_mode (GTK_TREE_SELECTION (object), g_value_get_enum (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_tree_selection_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + g_return_if_fail (GTK_IS_TREE_SELECTION (object)); + + switch (prop_id) + { + case PROP_MODE: + g_value_set_enum (value, gtk_tree_selection_get_mode (GTK_TREE_SELECTION (object))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +/** + * _gtk_tree_selection_new: + * + * Creates a new `GtkTreeSelection` object. This function should not be invoked, + * as each `GtkTreeView` will create its own `GtkTreeSelection`. + * + * Returns: A newly created `GtkTreeSelection` object. + * + * Deprecated: 4.10: Use GtkListView or GtkColumnView + **/ +GtkTreeSelection* +_gtk_tree_selection_new (void) +{ + GtkTreeSelection *selection; + + selection = g_object_new (GTK_TYPE_TREE_SELECTION, NULL); + + return selection; +} + +/** + * _gtk_tree_selection_new_with_tree_view: + * @tree_view: The `GtkTreeView`. + * + * Creates a new `GtkTreeSelection` object. This function should not be invoked, + * as each `GtkTreeView` will create its own `GtkTreeSelection`. + * + * Returns: A newly created `GtkTreeSelection` object. + * + * Deprecated: 4.10: Use GtkListView or GtkColumnView + **/ +GtkTreeSelection* +_gtk_tree_selection_new_with_tree_view (GtkTreeView *tree_view) +{ + GtkTreeSelection *selection; + + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL); + + selection = _gtk_tree_selection_new (); + _gtk_tree_selection_set_tree_view (selection, tree_view); + + return selection; +} + +/** + * _gtk_tree_selection_set_tree_view: + * @selection: A `GtkTreeSelection`. + * @tree_view: The `GtkTreeView`. + * + * Sets the `GtkTreeView` of @selection. This function should not be invoked, as + * it is used internally by `GtkTreeView`. + * + * Deprecated: 4.10: Use GtkListView or GtkColumnView + **/ +void +_gtk_tree_selection_set_tree_view (GtkTreeSelection *selection, + GtkTreeView *tree_view) +{ + + g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); + if (tree_view != NULL) + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + selection->tree_view = tree_view; +} + +/** + * gtk_tree_selection_set_mode: + * @selection: A `GtkTreeSelection`. + * @type: The selection mode + * + * Sets the selection mode of the @selection. If the previous type was + * %GTK_SELECTION_MULTIPLE, then the anchor is kept selected, if it was + * previously selected. + * + * Deprecated: 4.10: Use GtkListView or GtkColumnView + **/ +void +gtk_tree_selection_set_mode (GtkTreeSelection *selection, + GtkSelectionMode type) +{ + GtkTreeSelectionFunc tmp_func; + + g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); + + if (selection->type == type) + return; + + if (type == GTK_SELECTION_NONE) + { + /* We do this so that we unconditionally unset all rows + */ + tmp_func = selection->user_func; + selection->user_func = NULL; + gtk_tree_selection_unselect_all (selection); + selection->user_func = tmp_func; + + _gtk_tree_view_set_anchor_path (selection->tree_view, NULL); + } + else if (type == GTK_SELECTION_SINGLE || + type == GTK_SELECTION_BROWSE) + { + GtkTreeRBTree *tree = NULL; + GtkTreeRBNode *node = NULL; + int selected = FALSE; + GtkTreePath *anchor_path = NULL; + + anchor_path = _gtk_tree_view_get_anchor_path (selection->tree_view); + + if (anchor_path) + { + _gtk_tree_view_find_node (selection->tree_view, + anchor_path, + &tree, + &node); + + if (node && GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED)) + selected = TRUE; + } + + /* We do this so that we unconditionally unset all rows + */ + tmp_func = selection->user_func; + selection->user_func = NULL; + gtk_tree_selection_unselect_all (selection); + selection->user_func = tmp_func; + + if (node && selected) + _gtk_tree_selection_internal_select_node (selection, + node, + tree, + anchor_path, + 0, + FALSE); + if (anchor_path) + gtk_tree_path_free (anchor_path); + } + + selection->type = type; + + g_object_notify_by_pspec (G_OBJECT (selection), properties[PROP_MODE]); +} + +/** + * gtk_tree_selection_get_mode: + * @selection: a `GtkTreeSelection` + * + * Gets the selection mode for @selection. See + * gtk_tree_selection_set_mode(). + * + * Returns: the current selection mode + * + * Deprecated: 4.10: Use GtkListView or GtkColumnView + **/ +GtkSelectionMode +gtk_tree_selection_get_mode (GtkTreeSelection *selection) +{ + g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), GTK_SELECTION_SINGLE); + + return selection->type; +} + +/** + * gtk_tree_selection_set_select_function: + * @selection: A `GtkTreeSelection`. + * @func: (nullable): The selection function. May be %NULL + * @data: The selection function’s data. May be %NULL + * @destroy: The destroy function for user data. May be %NULL + * + * Sets the selection function. + * + * If set, this function is called before any node is selected or unselected, + * giving some control over which nodes are selected. The select function + * should return %TRUE if the state of the node may be toggled, and %FALSE + * if the state of the node should be left unchanged. + * + * Deprecated: 4.10: Use GtkListView or GtkColumnView + */ +void +gtk_tree_selection_set_select_function (GtkTreeSelection *selection, + GtkTreeSelectionFunc func, + gpointer data, + GDestroyNotify destroy) +{ + + g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); + + if (selection->destroy) + selection->destroy (selection->user_data); + + selection->user_func = func; + selection->user_data = data; + selection->destroy = destroy; +} + +/** + * gtk_tree_selection_get_select_function: (skip) + * @selection: A `GtkTreeSelection`. + * + * Returns the current selection function. + * + * Returns: The function. + * + * Deprecated: 4.10: Use GtkListView or GtkColumnView + **/ +GtkTreeSelectionFunc +gtk_tree_selection_get_select_function (GtkTreeSelection *selection) +{ + g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL); + + return selection->user_func; +} + +/** + * gtk_tree_selection_get_user_data: (skip) + * @selection: A `GtkTreeSelection`. + * + * Returns the user data for the selection function. + * + * Returns: The user data. + * + * Deprecated: 4.10: Use GtkListView or GtkColumnView + **/ +gpointer +gtk_tree_selection_get_user_data (GtkTreeSelection *selection) +{ + g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL); + + return selection->user_data; +} + +/** + * gtk_tree_selection_get_tree_view: + * @selection: A `GtkTreeSelection` + * + * Returns the tree view associated with @selection. + * + * Returns: (transfer none): A `GtkTreeView` + * + * Deprecated: 4.10: Use GtkListView or GtkColumnView + **/ +GtkTreeView * +gtk_tree_selection_get_tree_view (GtkTreeSelection *selection) +{ + g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL); + + return selection->tree_view; +} + +/** + * gtk_tree_selection_get_selected: + * @selection: A `GtkTreeSelection`. + * @model: (out) (optional) (transfer none): A pointer to set to the `GtkTreeModel` + * @iter: (out) (optional): The `GtkTreeIter` + * + * Sets @iter to the currently selected node if @selection is set to + * %GTK_SELECTION_SINGLE or %GTK_SELECTION_BROWSE. @iter may be NULL if you + * just want to test if @selection has any selected nodes. @model is filled + * with the current model as a convenience. This function will not work if you + * use @selection is %GTK_SELECTION_MULTIPLE. + * + * Returns: TRUE, if there is a selected node. + * + * Deprecated: 4.10: Use GtkListView or GtkColumnView + **/ +gboolean +gtk_tree_selection_get_selected (GtkTreeSelection *selection, + GtkTreeModel **model, + GtkTreeIter *iter) +{ + GtkTreeRBTree *tree; + GtkTreeRBNode *node; + GtkTreePath *anchor_path; + gboolean retval = FALSE; + gboolean found_node; + + g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), FALSE); + g_return_val_if_fail (selection->type != GTK_SELECTION_MULTIPLE, FALSE); + g_return_val_if_fail (selection->tree_view != NULL, FALSE); + + /* Clear the iter */ + if (iter) + memset (iter, 0, sizeof (GtkTreeIter)); + + if (model) + *model = gtk_tree_view_get_model (selection->tree_view); + + anchor_path = _gtk_tree_view_get_anchor_path (selection->tree_view); + + if (anchor_path == NULL) + return FALSE; + + found_node = !_gtk_tree_view_find_node (selection->tree_view, + anchor_path, + &tree, + &node); + + if (found_node && GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED)) + { + /* we only want to return the anchor if it exists in the rbtree and + * is selected. + */ + if (iter == NULL) + retval = TRUE; + else + retval = gtk_tree_model_get_iter (gtk_tree_view_get_model (selection->tree_view), + iter, + anchor_path); + } + else + { + /* We don't want to return the anchor if it isn't actually selected. + */ + retval = FALSE; + } + + gtk_tree_path_free (anchor_path); + + return retval; +} + +/** + * gtk_tree_selection_get_selected_rows: + * @selection: A `GtkTreeSelection`. + * @model: (out) (optional) (transfer none): A pointer to set to the `GtkTreeModel` + * + * Creates a list of path of all selected rows. Additionally, if you are + * planning on modifying the model after calling this function, you may + * want to convert the returned list into a list of `GtkTreeRowReference`s. + * To do this, you can use gtk_tree_row_reference_new(). + * + * To free the return value, use: + * |[ + * g_list_free_full (list, (GDestroyNotify) gtk_tree_path_free); + * ]| + * + * Returns: (element-type GtkTreePath) (transfer full): A `GList` containing a `GtkTreePath` for each selected row. + * + * Deprecated: 4.10: Use GtkListView or GtkColumnView + **/ +GList * +gtk_tree_selection_get_selected_rows (GtkTreeSelection *selection, + GtkTreeModel **model) +{ + GList *list = NULL; + GtkTreeRBTree *tree = NULL; + GtkTreeRBNode *node = NULL; + GtkTreePath *path; + + g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL); + g_return_val_if_fail (selection->tree_view != NULL, NULL); + + if (model) + *model = gtk_tree_view_get_model (selection->tree_view); + + tree = _gtk_tree_view_get_rbtree (selection->tree_view); + + if (tree == NULL || tree->root == NULL) + return NULL; + + if (selection->type == GTK_SELECTION_NONE) + return NULL; + else if (selection->type != GTK_SELECTION_MULTIPLE) + { + GtkTreeIter iter; + + if (gtk_tree_selection_get_selected (selection, NULL, &iter)) + { + path = gtk_tree_model_get_path (gtk_tree_view_get_model (selection->tree_view), &iter); + list = g_list_append (list, path); + + return list; + } + + return NULL; + } + + node = gtk_tree_rbtree_first (tree); + path = gtk_tree_path_new_first (); + + while (node != NULL) + { + if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED)) + list = g_list_prepend (list, gtk_tree_path_copy (path)); + + if (node->children) + { + tree = node->children; + node = gtk_tree_rbtree_first (tree); + + gtk_tree_path_append_index (path, 0); + } + else + { + gboolean done = FALSE; + + do + { + node = gtk_tree_rbtree_next (tree, node); + if (node != NULL) + { + done = TRUE; + gtk_tree_path_next (path); + } + else + { + node = tree->parent_node; + tree = tree->parent_tree; + + if (!tree) + { + gtk_tree_path_free (path); + + goto done; + } + + gtk_tree_path_up (path); + } + } + while (!done); + } + } + + gtk_tree_path_free (path); + + done: + return g_list_reverse (list); +} + +static void +gtk_tree_selection_count_selected_rows_helper (GtkTreeRBTree *tree, + GtkTreeRBNode *node, + gpointer data) +{ + int *count = (int *)data; + + g_return_if_fail (node != NULL); + + if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED)) + (*count)++; + + if (node->children) + gtk_tree_rbtree_traverse (node->children, node->children->root, + G_PRE_ORDER, + gtk_tree_selection_count_selected_rows_helper, data); +} + +/** + * gtk_tree_selection_count_selected_rows: + * @selection: A `GtkTreeSelection`. + * + * Returns the number of rows that have been selected in @tree. + * + * Returns: The number of rows selected. + * + * Deprecated: 4.10: Use GtkListView or GtkColumnView + **/ +int +gtk_tree_selection_count_selected_rows (GtkTreeSelection *selection) +{ + int count = 0; + GtkTreeRBTree *tree; + + g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), 0); + g_return_val_if_fail (selection->tree_view != NULL, 0); + + tree = _gtk_tree_view_get_rbtree (selection->tree_view); + + if (tree == NULL || tree->root == NULL) + return 0; + + if (selection->type == GTK_SELECTION_SINGLE || + selection->type == GTK_SELECTION_BROWSE) + { + if (gtk_tree_selection_get_selected (selection, NULL, NULL)) + return 1; + else + return 0; + } + + gtk_tree_rbtree_traverse (tree, tree->root, + G_PRE_ORDER, + gtk_tree_selection_count_selected_rows_helper, + &count); + + return count; +} + +/* gtk_tree_selection_selected_foreach helper */ +static void +model_changed (gpointer data) +{ + gboolean *stop = (gboolean *)data; + + *stop = TRUE; +} + +/** + * gtk_tree_selection_selected_foreach: + * @selection: A `GtkTreeSelection`. + * @func: (scope call): The function to call for each selected node. + * @data: user data to pass to the function. + * + * Calls a function for each selected node. Note that you cannot modify + * the tree or selection from within this function. As a result, + * gtk_tree_selection_get_selected_rows() might be more useful. + * + * Deprecated: 4.10: Use GtkListView or GtkColumnView + **/ +void +gtk_tree_selection_selected_foreach (GtkTreeSelection *selection, + GtkTreeSelectionForeachFunc func, + gpointer data) +{ + GtkTreePath *path; + GtkTreeRBTree *tree; + GtkTreeRBNode *node; + GtkTreeIter iter; + GtkTreeModel *model; + + gulong inserted_id, deleted_id, reordered_id, changed_id; + gboolean stop = FALSE; + + g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); + g_return_if_fail (selection->tree_view != NULL); + + tree = _gtk_tree_view_get_rbtree (selection->tree_view); + + if (func == NULL || tree == NULL || tree->root == NULL) + return; + + model = gtk_tree_view_get_model (selection->tree_view); + + if (selection->type == GTK_SELECTION_SINGLE || + selection->type == GTK_SELECTION_BROWSE) + { + path = _gtk_tree_view_get_anchor_path (selection->tree_view); + + if (path) + { + gtk_tree_model_get_iter (model, &iter, path); + (* func) (model, path, &iter, data); + gtk_tree_path_free (path); + } + return; + } + + node = gtk_tree_rbtree_first (tree); + + g_object_ref (model); + + /* connect to signals to monitor changes in treemodel */ + inserted_id = g_signal_connect_swapped (model, "row-inserted", + G_CALLBACK (model_changed), + &stop); + deleted_id = g_signal_connect_swapped (model, "row-deleted", + G_CALLBACK (model_changed), + &stop); + reordered_id = g_signal_connect_swapped (model, "rows-reordered", + G_CALLBACK (model_changed), + &stop); + changed_id = g_signal_connect_swapped (selection->tree_view, "notify::model", + G_CALLBACK (model_changed), + &stop); + + /* find the node internally */ + path = gtk_tree_path_new_first (); + + while (node != NULL) + { + if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED)) + { + gtk_tree_model_get_iter (model, &iter, path); + (* func) (model, path, &iter, data); + } + + if (stop) + goto out; + + if (node->children) + { + tree = node->children; + node = gtk_tree_rbtree_first (tree); + + gtk_tree_path_append_index (path, 0); + } + else + { + gboolean done = FALSE; + + do + { + node = gtk_tree_rbtree_next (tree, node); + if (node != NULL) + { + done = TRUE; + gtk_tree_path_next (path); + } + else + { + node = tree->parent_node; + tree = tree->parent_tree; + + if (tree == NULL) + { + /* we've run out of tree */ + /* We're done with this function */ + + goto out; + } + + gtk_tree_path_up (path); + } + } + while (!done); + } + } + +out: + if (path) + gtk_tree_path_free (path); + + g_signal_handler_disconnect (model, inserted_id); + g_signal_handler_disconnect (model, deleted_id); + g_signal_handler_disconnect (model, reordered_id); + g_signal_handler_disconnect (selection->tree_view, changed_id); + g_object_unref (model); + + /* check if we have to spew a scary message */ + if (stop) + g_warning ("The model has been modified from within gtk_tree_selection_selected_foreach.\n" + "This function is for observing the selections of the tree only. If\n" + "you are trying to get all selected items from the tree, try using\n" + "gtk_tree_selection_get_selected_rows instead."); +} + +/** + * gtk_tree_selection_select_path: + * @selection: A `GtkTreeSelection`. + * @path: The `GtkTreePath` to be selected. + * + * Select the row at @path. + * + * Deprecated: 4.10: Use GtkListView or GtkColumnView + **/ +void +gtk_tree_selection_select_path (GtkTreeSelection *selection, + GtkTreePath *path) +{ + GtkTreeRBNode *node; + GtkTreeRBTree *tree; + gboolean ret; + GtkTreeSelectMode mode = 0; + + g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); + g_return_if_fail (selection->tree_view != NULL); + g_return_if_fail (path != NULL); + + ret = _gtk_tree_view_find_node (selection->tree_view, + path, + &tree, + &node); + + if (node == NULL || GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED) || + ret == TRUE) + return; + + if (selection->type == GTK_SELECTION_MULTIPLE) + mode = GTK_TREE_SELECT_MODE_TOGGLE; + + _gtk_tree_selection_internal_select_node (selection, + node, + tree, + path, + mode, + FALSE); +} + +/** + * gtk_tree_selection_unselect_path: + * @selection: A `GtkTreeSelection`. + * @path: The `GtkTreePath` to be unselected. + * + * Unselects the row at @path. + * + * Deprecated: 4.10: Use GtkListView or GtkColumnView + **/ +void +gtk_tree_selection_unselect_path (GtkTreeSelection *selection, + GtkTreePath *path) +{ + GtkTreeRBNode *node; + GtkTreeRBTree *tree; + gboolean ret; + + g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); + g_return_if_fail (selection->tree_view != NULL); + g_return_if_fail (path != NULL); + + ret = _gtk_tree_view_find_node (selection->tree_view, + path, + &tree, + &node); + + if (node == NULL || !GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED) || + ret == TRUE) + return; + + _gtk_tree_selection_internal_select_node (selection, + node, + tree, + path, + GTK_TREE_SELECT_MODE_TOGGLE, + TRUE); +} + +/** + * gtk_tree_selection_select_iter: + * @selection: A `GtkTreeSelection`. + * @iter: The `GtkTreeIter` to be selected. + * + * Selects the specified iterator. + * + * Deprecated: 4.10: Use GtkListView or GtkColumnView + **/ +void +gtk_tree_selection_select_iter (GtkTreeSelection *selection, + GtkTreeIter *iter) +{ + GtkTreePath *path; + GtkTreeModel *model; + + g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); + g_return_if_fail (selection->tree_view != NULL); + + model = gtk_tree_view_get_model (selection->tree_view); + g_return_if_fail (model != NULL); + g_return_if_fail (iter != NULL); + + path = gtk_tree_model_get_path (model, iter); + + if (path == NULL) + return; + + gtk_tree_selection_select_path (selection, path); + gtk_tree_path_free (path); +} + + +/** + * gtk_tree_selection_unselect_iter: + * @selection: A `GtkTreeSelection`. + * @iter: The `GtkTreeIter` to be unselected. + * + * Unselects the specified iterator. + * + * Deprecated: 4.10: Use GtkListView or GtkColumnView + **/ +void +gtk_tree_selection_unselect_iter (GtkTreeSelection *selection, + GtkTreeIter *iter) +{ + GtkTreePath *path; + GtkTreeModel *model; + + g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); + g_return_if_fail (selection->tree_view != NULL); + + model = gtk_tree_view_get_model (selection->tree_view); + g_return_if_fail (model != NULL); + g_return_if_fail (iter != NULL); + + path = gtk_tree_model_get_path (model, iter); + + if (path == NULL) + return; + + gtk_tree_selection_unselect_path (selection, path); + gtk_tree_path_free (path); +} + +/** + * gtk_tree_selection_path_is_selected: + * @selection: A `GtkTreeSelection`. + * @path: A `GtkTreePath` to check selection on. + * + * Returns %TRUE if the row pointed to by @path is currently selected. If @path + * does not point to a valid location, %FALSE is returned + * + * Returns: %TRUE if @path is selected. + * + * Deprecated: 4.10: Use GtkListView or GtkColumnView + **/ +gboolean +gtk_tree_selection_path_is_selected (GtkTreeSelection *selection, + GtkTreePath *path) +{ + GtkTreeRBNode *node; + GtkTreeRBTree *tree; + gboolean ret; + + g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), FALSE); + g_return_val_if_fail (path != NULL, FALSE); + g_return_val_if_fail (selection->tree_view != NULL, FALSE); + + if (gtk_tree_view_get_model (selection->tree_view) == NULL) + return FALSE; + + ret = _gtk_tree_view_find_node (selection->tree_view, + path, + &tree, + &node); + + if ((node == NULL) || !GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED) || + ret == TRUE) + return FALSE; + + return TRUE; +} + +/** + * gtk_tree_selection_iter_is_selected: + * @selection: A `GtkTreeSelection` + * @iter: A valid `GtkTreeIter` + * + * Returns %TRUE if the row at @iter is currently selected. + * + * Returns: %TRUE, if @iter is selected + * + * Deprecated: 4.10: Use GtkListView or GtkColumnView + **/ +gboolean +gtk_tree_selection_iter_is_selected (GtkTreeSelection *selection, + GtkTreeIter *iter) +{ + GtkTreePath *path; + GtkTreeModel *model; + gboolean retval; + + g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), FALSE); + g_return_val_if_fail (iter != NULL, FALSE); + g_return_val_if_fail (selection->tree_view != NULL, FALSE); + + model = gtk_tree_view_get_model (selection->tree_view); + g_return_val_if_fail (model != NULL, FALSE); + + path = gtk_tree_model_get_path (model, iter); + if (path == NULL) + return FALSE; + + retval = gtk_tree_selection_path_is_selected (selection, path); + gtk_tree_path_free (path); + + return retval; +} + + +/* Wish I was in python, right now... */ +struct _TempTuple { + GtkTreeSelection *selection; + int dirty; +}; + +static void +select_all_helper (GtkTreeRBTree *tree, + GtkTreeRBNode *node, + gpointer data) +{ + struct _TempTuple *tuple = data; + + if (node->children) + gtk_tree_rbtree_traverse (node->children, + node->children->root, + G_PRE_ORDER, + select_all_helper, + data); + if (!GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED)) + { + tuple->dirty = gtk_tree_selection_real_select_node (tuple->selection, tree, node, TRUE) || tuple->dirty; + } +} + + +/* We have a real_{un,}select_all function that doesn't emit the signal, so we + * can use it in other places without fear of the signal being emitted. + */ +static int +gtk_tree_selection_real_select_all (GtkTreeSelection *selection) +{ + struct _TempTuple *tuple; + GtkTreeRBTree *tree; + + tree = _gtk_tree_view_get_rbtree (selection->tree_view); + + if (tree == NULL) + return FALSE; + + /* Mark all nodes selected */ + tuple = g_new (struct _TempTuple, 1); + tuple->selection = selection; + tuple->dirty = FALSE; + + gtk_tree_rbtree_traverse (tree, tree->root, + G_PRE_ORDER, + select_all_helper, + tuple); + if (tuple->dirty) + { + g_free (tuple); + return TRUE; + } + g_free (tuple); + return FALSE; +} + +/** + * gtk_tree_selection_select_all: + * @selection: A `GtkTreeSelection`. + * + * Selects all the nodes. @selection must be set to %GTK_SELECTION_MULTIPLE + * mode. + * + * Deprecated: 4.10: Use GtkListView or GtkColumnView + **/ +void +gtk_tree_selection_select_all (GtkTreeSelection *selection) +{ + + g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); + g_return_if_fail (selection->tree_view != NULL); + + if (_gtk_tree_view_get_rbtree (selection->tree_view) == NULL || + gtk_tree_view_get_model (selection->tree_view) == NULL) + return; + + g_return_if_fail (selection->type == GTK_SELECTION_MULTIPLE); + + if (gtk_tree_selection_real_select_all (selection)) + g_signal_emit (selection, tree_selection_signals[CHANGED], 0); +} + +static void +unselect_all_helper (GtkTreeRBTree *tree, + GtkTreeRBNode *node, + gpointer data) +{ + struct _TempTuple *tuple = data; + + if (node->children) + gtk_tree_rbtree_traverse (node->children, + node->children->root, + G_PRE_ORDER, + unselect_all_helper, + data); + if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED)) + { + tuple->dirty = gtk_tree_selection_real_select_node (tuple->selection, tree, node, FALSE) || tuple->dirty; + } +} + +static gboolean +gtk_tree_selection_real_unselect_all (GtkTreeSelection *selection) +{ + struct _TempTuple *tuple; + + if (selection->type == GTK_SELECTION_SINGLE || + selection->type == GTK_SELECTION_BROWSE) + { + GtkTreeRBTree *tree = NULL; + GtkTreeRBNode *node = NULL; + GtkTreePath *anchor_path; + + anchor_path = _gtk_tree_view_get_anchor_path (selection->tree_view); + + if (anchor_path == NULL) + return FALSE; + + _gtk_tree_view_find_node (selection->tree_view, + anchor_path, + &tree, + &node); + + gtk_tree_path_free (anchor_path); + + if (tree == NULL) + return FALSE; + + if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED)) + { + if (gtk_tree_selection_real_select_node (selection, tree, node, FALSE)) + { + _gtk_tree_view_set_anchor_path (selection->tree_view, NULL); + return TRUE; + } + } + return FALSE; + } + else + { + GtkTreeRBTree *tree; + + tuple = g_new (struct _TempTuple, 1); + tuple->selection = selection; + tuple->dirty = FALSE; + + tree = _gtk_tree_view_get_rbtree (selection->tree_view); + gtk_tree_rbtree_traverse (tree, tree->root, + G_PRE_ORDER, + unselect_all_helper, + tuple); + + if (tuple->dirty) + { + g_free (tuple); + return TRUE; + } + g_free (tuple); + return FALSE; + } +} + +/** + * gtk_tree_selection_unselect_all: + * @selection: A `GtkTreeSelection`. + * + * Unselects all the nodes. + * + * Deprecated: 4.10: Use GtkListView or GtkColumnView + **/ +void +gtk_tree_selection_unselect_all (GtkTreeSelection *selection) +{ + + g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); + g_return_if_fail (selection->tree_view != NULL); + + if (_gtk_tree_view_get_rbtree (selection->tree_view) == NULL || + gtk_tree_view_get_model (selection->tree_view) == NULL) + return; + + if (gtk_tree_selection_real_unselect_all (selection)) + g_signal_emit (selection, tree_selection_signals[CHANGED], 0); +} + +enum +{ + RANGE_SELECT, + RANGE_UNSELECT +}; + +static int +gtk_tree_selection_real_modify_range (GtkTreeSelection *selection, + int mode, + GtkTreePath *start_path, + GtkTreePath *end_path) +{ + GtkTreeRBNode *start_node = NULL, *end_node = NULL; + GtkTreeRBTree *start_tree, *end_tree; + GtkTreePath *anchor_path = NULL; + gboolean dirty = FALSE; + + switch (gtk_tree_path_compare (start_path, end_path)) + { + case 1: + _gtk_tree_view_find_node (selection->tree_view, + end_path, + &start_tree, + &start_node); + _gtk_tree_view_find_node (selection->tree_view, + start_path, + &end_tree, + &end_node); + anchor_path = start_path; + break; + case 0: + _gtk_tree_view_find_node (selection->tree_view, + start_path, + &start_tree, + &start_node); + end_tree = start_tree; + end_node = start_node; + anchor_path = start_path; + break; + case -1: + _gtk_tree_view_find_node (selection->tree_view, + start_path, + &start_tree, + &start_node); + _gtk_tree_view_find_node (selection->tree_view, + end_path, + &end_tree, + &end_node); + anchor_path = start_path; + break; + default: + g_assert_not_reached (); + break; + } + + /* Invalid start or end node? */ + if (start_node == NULL || end_node == NULL) + return dirty; + + if (anchor_path) + _gtk_tree_view_set_anchor_path (selection->tree_view, anchor_path); + + do + { + dirty |= gtk_tree_selection_real_select_node (selection, start_tree, start_node, (mode == RANGE_SELECT)?TRUE:FALSE); + + if (start_node == end_node) + break; + + if (start_node->children) + { + start_tree = start_node->children; + start_node = gtk_tree_rbtree_first (start_tree); + } + else + { + gtk_tree_rbtree_next_full (start_tree, start_node, &start_tree, &start_node); + if (start_tree == NULL) + { + /* we just ran out of tree. That means someone passed in bogus values. + */ + return dirty; + } + } + } + while (TRUE); + + return dirty; +} + +/** + * gtk_tree_selection_select_range: + * @selection: A `GtkTreeSelection`. + * @start_path: The initial node of the range. + * @end_path: The final node of the range. + * + * Selects a range of nodes, determined by @start_path and @end_path inclusive. + * @selection must be set to %GTK_SELECTION_MULTIPLE mode. + * + * Deprecated: 4.10: Use GtkListView or GtkColumnView + **/ +void +gtk_tree_selection_select_range (GtkTreeSelection *selection, + GtkTreePath *start_path, + GtkTreePath *end_path) +{ + + g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); + g_return_if_fail (selection->tree_view != NULL); + g_return_if_fail (selection->type == GTK_SELECTION_MULTIPLE); + g_return_if_fail (gtk_tree_view_get_model (selection->tree_view) != NULL); + + if (gtk_tree_selection_real_modify_range (selection, RANGE_SELECT, start_path, end_path)) + g_signal_emit (selection, tree_selection_signals[CHANGED], 0); +} + +/** + * gtk_tree_selection_unselect_range: + * @selection: A `GtkTreeSelection`. + * @start_path: The initial node of the range. + * @end_path: The initial node of the range. + * + * Unselects a range of nodes, determined by @start_path and @end_path + * inclusive. + * + * Deprecated: 4.10: Use GtkListView or GtkColumnView + **/ +void +gtk_tree_selection_unselect_range (GtkTreeSelection *selection, + GtkTreePath *start_path, + GtkTreePath *end_path) +{ + + g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); + g_return_if_fail (selection->tree_view != NULL); + g_return_if_fail (gtk_tree_view_get_model (selection->tree_view) != NULL); + + if (gtk_tree_selection_real_modify_range (selection, RANGE_UNSELECT, start_path, end_path)) + g_signal_emit (selection, tree_selection_signals[CHANGED], 0); +} + +gboolean +_gtk_tree_selection_row_is_selectable (GtkTreeSelection *selection, + GtkTreeRBNode *node, + GtkTreePath *path) +{ + GtkTreeIter iter; + GtkTreeModel *model; + GtkTreeViewRowSeparatorFunc separator_func; + gpointer separator_data; + gboolean sensitive = FALSE; + + model = gtk_tree_view_get_model (selection->tree_view); + + _gtk_tree_view_get_row_separator_func (selection->tree_view, + &separator_func, &separator_data); + + if (!gtk_tree_model_get_iter (model, &iter, path)) + sensitive = TRUE; + + if (!sensitive && separator_func) + { + /* never allow separators to be selected */ + if ((* separator_func) (model, &iter, separator_data)) + return FALSE; + } + + if (selection->user_func) + return (*selection->user_func) (selection, model, path, + GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED), + selection->user_data); + else + return TRUE; +} + + +/* Called internally by gtktreeview.c It handles actually selecting the tree. + */ + +/* + * docs about the “override_browse_mode”, we set this flag when we want to + * unset select the node and override the select browse mode behaviour (that is + * 'one node should *always* be selected'). + */ +void +_gtk_tree_selection_internal_select_node (GtkTreeSelection *selection, + GtkTreeRBNode *node, + GtkTreeRBTree *tree, + GtkTreePath *path, + GtkTreeSelectMode mode, + gboolean override_browse_mode) +{ + int flags; + int dirty = FALSE; + GtkTreePath *anchor_path = NULL; + + if (selection->type == GTK_SELECTION_NONE) + return; + + anchor_path = _gtk_tree_view_get_anchor_path (selection->tree_view); + + if (selection->type == GTK_SELECTION_SINGLE || + selection->type == GTK_SELECTION_BROWSE) + { + /* just unselect */ + if (selection->type == GTK_SELECTION_BROWSE && override_browse_mode) + { + dirty = gtk_tree_selection_real_unselect_all (selection); + } + /* Did we try to select the same node again? */ + else if (selection->type == GTK_SELECTION_SINGLE && + anchor_path && gtk_tree_path_compare (path, anchor_path) == 0) + { + if ((mode & GTK_TREE_SELECT_MODE_TOGGLE) == GTK_TREE_SELECT_MODE_TOGGLE) + { + dirty = gtk_tree_selection_real_unselect_all (selection); + } + } + else + { + if (anchor_path) + { + /* We only want to select the new node if we can unselect the old one, + * and we can select the new one. */ + dirty = _gtk_tree_selection_row_is_selectable (selection, node, path); + + /* if dirty is FALSE, we weren't able to select the new one, otherwise, we try to + * unselect the new one + */ + if (dirty) + dirty = gtk_tree_selection_real_unselect_all (selection); + + /* if dirty is TRUE at this point, we successfully unselected the + * old one, and can then select the new one */ + if (dirty) + { + + _gtk_tree_view_set_anchor_path (selection->tree_view, NULL); + + if (gtk_tree_selection_real_select_node (selection, tree, node, TRUE)) + _gtk_tree_view_set_anchor_path (selection->tree_view, path); + } + } + else + { + if (gtk_tree_selection_real_select_node (selection, tree, node, TRUE)) + { + dirty = TRUE; + + _gtk_tree_view_set_anchor_path (selection->tree_view, path); + } + } + } + } + else if (selection->type == GTK_SELECTION_MULTIPLE) + { + if ((mode & GTK_TREE_SELECT_MODE_EXTEND) == GTK_TREE_SELECT_MODE_EXTEND + && (anchor_path == NULL)) + { + _gtk_tree_view_set_anchor_path (selection->tree_view, path); + + dirty = gtk_tree_selection_real_select_node (selection, tree, node, TRUE); + } + else if ((mode & (GTK_TREE_SELECT_MODE_EXTEND | GTK_TREE_SELECT_MODE_TOGGLE)) == (GTK_TREE_SELECT_MODE_EXTEND | GTK_TREE_SELECT_MODE_TOGGLE)) + { + gtk_tree_selection_select_range (selection, + anchor_path, + path); + } + else if ((mode & GTK_TREE_SELECT_MODE_TOGGLE) == GTK_TREE_SELECT_MODE_TOGGLE) + { + flags = node->flags; + + _gtk_tree_view_set_anchor_path (selection->tree_view, path); + + if ((flags & GTK_TREE_RBNODE_IS_SELECTED) == GTK_TREE_RBNODE_IS_SELECTED) + dirty |= gtk_tree_selection_real_select_node (selection, tree, node, FALSE); + else + dirty |= gtk_tree_selection_real_select_node (selection, tree, node, TRUE); + } + else if ((mode & GTK_TREE_SELECT_MODE_EXTEND) == GTK_TREE_SELECT_MODE_EXTEND) + { + dirty = gtk_tree_selection_real_unselect_all (selection); + dirty |= gtk_tree_selection_real_modify_range (selection, + RANGE_SELECT, + anchor_path, + path); + } + else + { + dirty = gtk_tree_selection_real_unselect_all (selection); + + _gtk_tree_view_set_anchor_path (selection->tree_view, path); + + dirty |= gtk_tree_selection_real_select_node (selection, tree, node, TRUE); + } + } + + if (anchor_path) + gtk_tree_path_free (anchor_path); + + if (dirty) + g_signal_emit (selection, tree_selection_signals[CHANGED], 0); +} + + +void +_gtk_tree_selection_emit_changed (GtkTreeSelection *selection) +{ + g_signal_emit (selection, tree_selection_signals[CHANGED], 0); +} + +/* NOTE: Any {un,}selection ever done _MUST_ be done through this function! + */ + +static int +gtk_tree_selection_real_select_node (GtkTreeSelection *selection, + GtkTreeRBTree *tree, + GtkTreeRBNode *node, + gboolean select) +{ + gboolean toggle = FALSE; + GtkTreePath *path = NULL; + + g_return_val_if_fail (node != NULL, FALSE); + + select = !! select; + + if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED) != select) + { + path = _gtk_tree_path_new_from_rbtree (tree, node); + toggle = _gtk_tree_selection_row_is_selectable (selection, node, path); + gtk_tree_path_free (path); + } + + if (toggle) + { + if (!GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED)) + { + GTK_TREE_RBNODE_SET_FLAG (node, GTK_TREE_RBNODE_IS_SELECTED); + } + else + { + GTK_TREE_RBNODE_UNSET_FLAG (node, GTK_TREE_RBNODE_IS_SELECTED); + } + + gtk_widget_queue_draw (GTK_WIDGET (selection->tree_view)); + + return TRUE; + } + + return FALSE; +} diff --git a/gtk/deprecated/gtktreeselection.h b/gtk/deprecated/gtktreeselection.h new file mode 100644 index 0000000000..002646425e --- /dev/null +++ b/gtk/deprecated/gtktreeselection.h @@ -0,0 +1,144 @@ +/* gtktreeselection.h + * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#ifndef __GTK_TREE_SELECTION_H__ +#define __GTK_TREE_SELECTION_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + + +#define GTK_TYPE_TREE_SELECTION (gtk_tree_selection_get_type ()) +#define GTK_TREE_SELECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_TREE_SELECTION, GtkTreeSelection)) +#define GTK_IS_TREE_SELECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_TREE_SELECTION)) + +/** + * GtkTreeSelectionFunc: + * @selection: A `GtkTreeSelection` + * @model: A `GtkTreeModel` being viewed + * @path: The `GtkTreePath` of the row in question + * @path_currently_selected: %TRUE, if the path is currently selected + * @data: (closure): user data + * + * A function used by gtk_tree_selection_set_select_function() to filter + * whether or not a row may be selected. It is called whenever a row's + * state might change. + * + * A return value of %TRUE indicates to @selection that it is okay to + * change the selection. + * + * Returns: %TRUE, if the selection state of the row can be toggled + */ +typedef gboolean (* GtkTreeSelectionFunc) (GtkTreeSelection *selection, + GtkTreeModel *model, + GtkTreePath *path, + gboolean path_currently_selected, + gpointer data); + +/** + * GtkTreeSelectionForeachFunc: + * @model: The `GtkTreeModel` being viewed + * @path: The `GtkTreePath` of a selected row + * @iter: A `GtkTreeIter` pointing to a selected row + * @data: (closure): user data + * + * A function used by gtk_tree_selection_selected_foreach() to map all + * selected rows. It will be called on every selected row in the view. + */ +typedef void (* GtkTreeSelectionForeachFunc) (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data); + + +GDK_AVAILABLE_IN_ALL +GType gtk_tree_selection_get_type (void) G_GNUC_CONST; + +GDK_DEPRECATED_IN_4_10 +void gtk_tree_selection_set_mode (GtkTreeSelection *selection, + GtkSelectionMode type); +GDK_DEPRECATED_IN_4_10 +GtkSelectionMode gtk_tree_selection_get_mode (GtkTreeSelection *selection); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_selection_set_select_function (GtkTreeSelection *selection, + GtkTreeSelectionFunc func, + gpointer data, + GDestroyNotify destroy); +GDK_DEPRECATED_IN_4_10 +gpointer gtk_tree_selection_get_user_data (GtkTreeSelection *selection); +GDK_DEPRECATED_IN_4_10 +GtkTreeView* gtk_tree_selection_get_tree_view (GtkTreeSelection *selection); + +GDK_DEPRECATED_IN_4_10 +GtkTreeSelectionFunc gtk_tree_selection_get_select_function (GtkTreeSelection *selection); + +/* Only meaningful if GTK_SELECTION_SINGLE or GTK_SELECTION_BROWSE is set */ +/* Use selected_foreach or get_selected_rows for GTK_SELECTION_MULTIPLE */ +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_selection_get_selected (GtkTreeSelection *selection, + GtkTreeModel **model, + GtkTreeIter *iter); +GDK_DEPRECATED_IN_4_10 +GList * gtk_tree_selection_get_selected_rows (GtkTreeSelection *selection, + GtkTreeModel **model); +GDK_DEPRECATED_IN_4_10 +int gtk_tree_selection_count_selected_rows (GtkTreeSelection *selection); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_selection_selected_foreach (GtkTreeSelection *selection, + GtkTreeSelectionForeachFunc func, + gpointer data); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_selection_select_path (GtkTreeSelection *selection, + GtkTreePath *path); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_selection_unselect_path (GtkTreeSelection *selection, + GtkTreePath *path); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_selection_select_iter (GtkTreeSelection *selection, + GtkTreeIter *iter); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_selection_unselect_iter (GtkTreeSelection *selection, + GtkTreeIter *iter); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_selection_path_is_selected (GtkTreeSelection *selection, + GtkTreePath *path); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_selection_iter_is_selected (GtkTreeSelection *selection, + GtkTreeIter *iter); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_selection_select_all (GtkTreeSelection *selection); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_selection_unselect_all (GtkTreeSelection *selection); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_selection_select_range (GtkTreeSelection *selection, + GtkTreePath *start_path, + GtkTreePath *end_path); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_selection_unselect_range (GtkTreeSelection *selection, + GtkTreePath *start_path, + GtkTreePath *end_path); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkTreeSelection, g_object_unref) + +G_END_DECLS + +#endif /* __GTK_TREE_SELECTION_H__ */ diff --git a/gtk/deprecated/gtktreesortable.c b/gtk/deprecated/gtktreesortable.c new file mode 100644 index 0000000000..aa7d9c35ee --- /dev/null +++ b/gtk/deprecated/gtktreesortable.c @@ -0,0 +1,277 @@ +/* gtktreesortable.c + * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + + +#include "config.h" +#include "gtktreesortable.h" +#include "gtkmarshalers.h" +#include "gtkprivate.h" + + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + +/** + * GtkTreeSortable: + * + * The interface for sortable models used by GtkTreeView + * + * `GtkTreeSortable` is an interface to be implemented by tree models which + * support sorting. The `GtkTreeView` uses the methods provided by this interface + * to sort the model. + */ + + +static void gtk_tree_sortable_base_init (gpointer g_class); + +GType +gtk_tree_sortable_get_type (void) +{ + static GType tree_sortable_type = 0; + + if (! tree_sortable_type) + { + const GTypeInfo tree_sortable_info = + { + sizeof (GtkTreeSortableIface), /* class_size */ + gtk_tree_sortable_base_init, /* base_init */ + NULL, /* base_finalize */ + NULL, + NULL, /* class_finalize */ + NULL, /* class_data */ + 0, + 0, + NULL + }; + + tree_sortable_type = + g_type_register_static (G_TYPE_INTERFACE, I_("GtkTreeSortable"), + &tree_sortable_info, 0); + + g_type_interface_add_prerequisite (tree_sortable_type, GTK_TYPE_TREE_MODEL); + } + + return tree_sortable_type; +} + +static void +gtk_tree_sortable_base_init (gpointer g_class) +{ + static gboolean initialized = FALSE; + + if (! initialized) + { + /** + * GtkTreeSortable::sort-column-changed: + * @sortable: the object on which the signal is emitted + * + * The ::sort-column-changed signal is emitted when the sort column + * or sort order of @sortable is changed. The signal is emitted before + * the contents of @sortable are resorted. + */ + g_signal_new (I_("sort-column-changed"), + GTK_TYPE_TREE_SORTABLE, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkTreeSortableIface, sort_column_changed), + NULL, NULL, + NULL, + G_TYPE_NONE, 0); + initialized = TRUE; + } +} + +/** + * gtk_tree_sortable_sort_column_changed: + * @sortable: A `GtkTreeSortable` + * + * Emits a `GtkTreeSortable::sort-column-changed` signal on @sortable. + * + * Deprecated: 4.10 + */ +void +gtk_tree_sortable_sort_column_changed (GtkTreeSortable *sortable) +{ + g_return_if_fail (GTK_IS_TREE_SORTABLE (sortable)); + + g_signal_emit_by_name (sortable, "sort-column-changed"); +} + +/** + * gtk_tree_sortable_get_sort_column_id: + * @sortable: A `GtkTreeSortable` + * @sort_column_id: (out): The sort column id to be filled in + * @order: (out): The `GtkSortType` to be filled in + * + * Fills in @sort_column_id and @order with the current sort column and the + * order. It returns %TRUE unless the @sort_column_id is + * %GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID or + * %GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID. + * + * Returns: %TRUE if the sort column is not one of the special sort + * column ids. + * + * Deprecated: 4.10 + **/ +gboolean +gtk_tree_sortable_get_sort_column_id (GtkTreeSortable *sortable, + int *sort_column_id, + GtkSortType *order) +{ + GtkTreeSortableIface *iface; + + g_return_val_if_fail (GTK_IS_TREE_SORTABLE (sortable), FALSE); + + iface = GTK_TREE_SORTABLE_GET_IFACE (sortable); + + g_return_val_if_fail (iface != NULL, FALSE); + g_return_val_if_fail (iface->get_sort_column_id != NULL, FALSE); + + return (* iface->get_sort_column_id) (sortable, sort_column_id, order); +} + +/** + * gtk_tree_sortable_set_sort_column_id: + * @sortable: A `GtkTreeSortable` + * @sort_column_id: the sort column id to set + * @order: The sort order of the column + * + * Sets the current sort column to be @sort_column_id. The @sortable will + * resort itself to reflect this change, after emitting a + * `GtkTreeSortable::sort-column-changed` signal. @sort_column_id may either be + * a regular column id, or one of the following special values: + * + * - %GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID: the default sort function + * will be used, if it is set + * + * - %GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID: no sorting will occur + * + * Deprecated: 4.10 + */ +void +gtk_tree_sortable_set_sort_column_id (GtkTreeSortable *sortable, + int sort_column_id, + GtkSortType order) +{ + GtkTreeSortableIface *iface; + + g_return_if_fail (GTK_IS_TREE_SORTABLE (sortable)); + + iface = GTK_TREE_SORTABLE_GET_IFACE (sortable); + + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->set_sort_column_id != NULL); + + (* iface->set_sort_column_id) (sortable, sort_column_id, order); +} + +/** + * gtk_tree_sortable_set_sort_func: + * @sortable: A `GtkTreeSortable` + * @sort_column_id: the sort column id to set the function for + * @sort_func: The comparison function + * @user_data: (closure): User data to pass to @sort_func + * @destroy: (nullable): Destroy notifier of @user_data + * + * Sets the comparison function used when sorting to be @sort_func. If the + * current sort column id of @sortable is the same as @sort_column_id, then + * the model will sort using this function. + * + * Deprecated: 4.10 + */ +void +gtk_tree_sortable_set_sort_func (GtkTreeSortable *sortable, + int sort_column_id, + GtkTreeIterCompareFunc sort_func, + gpointer user_data, + GDestroyNotify destroy) +{ + GtkTreeSortableIface *iface; + + g_return_if_fail (GTK_IS_TREE_SORTABLE (sortable)); + g_return_if_fail (sort_func != NULL); + + iface = GTK_TREE_SORTABLE_GET_IFACE (sortable); + + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->set_sort_func != NULL); + g_return_if_fail (sort_column_id >= 0); + + (* iface->set_sort_func) (sortable, sort_column_id, sort_func, user_data, destroy); +} + +/** + * gtk_tree_sortable_set_default_sort_func: + * @sortable: A `GtkTreeSortable` + * @sort_func: The comparison function + * @user_data: (closure): User data to pass to @sort_func + * @destroy: (nullable): Destroy notifier of @user_data + * + * Sets the default comparison function used when sorting to be @sort_func. + * If the current sort column id of @sortable is + * %GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, then the model will sort using + * this function. + * + * If @sort_func is %NULL, then there will be no default comparison function. + * This means that once the model has been sorted, it can’t go back to the + * default state. In this case, when the current sort column id of @sortable + * is %GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, the model will be unsorted. + * + * Deprecated: 4.10 + */ +void +gtk_tree_sortable_set_default_sort_func (GtkTreeSortable *sortable, + GtkTreeIterCompareFunc sort_func, + gpointer user_data, + GDestroyNotify destroy) +{ + GtkTreeSortableIface *iface; + + g_return_if_fail (GTK_IS_TREE_SORTABLE (sortable)); + + iface = GTK_TREE_SORTABLE_GET_IFACE (sortable); + + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->set_default_sort_func != NULL); + + (* iface->set_default_sort_func) (sortable, sort_func, user_data, destroy); +} + +/** + * gtk_tree_sortable_has_default_sort_func: + * @sortable: A `GtkTreeSortable` + * + * Returns %TRUE if the model has a default sort function. This is used + * primarily by GtkTreeViewColumns in order to determine if a model can + * go back to the default state, or not. + * + * Returns: %TRUE, if the model has a default sort function + * + * Deprecated: 4.10 + */ +gboolean +gtk_tree_sortable_has_default_sort_func (GtkTreeSortable *sortable) +{ + GtkTreeSortableIface *iface; + + g_return_val_if_fail (GTK_IS_TREE_SORTABLE (sortable), FALSE); + + iface = GTK_TREE_SORTABLE_GET_IFACE (sortable); + + g_return_val_if_fail (iface != NULL, FALSE); + g_return_val_if_fail (iface->has_default_sort_func != NULL, FALSE); + + return (* iface->has_default_sort_func) (sortable); +} diff --git a/gtk/deprecated/gtktreesortable.h b/gtk/deprecated/gtktreesortable.h new file mode 100644 index 0000000000..4b74c5a6a7 --- /dev/null +++ b/gtk/deprecated/gtktreesortable.h @@ -0,0 +1,164 @@ +/* gtktreesortable.h + * Copyright (C) 2001 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#ifndef __GTK_TREE_SORTABLE_H__ +#define __GTK_TREE_SORTABLE_H__ + + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include + + +G_BEGIN_DECLS + +#define GTK_TYPE_TREE_SORTABLE (gtk_tree_sortable_get_type ()) +#define GTK_TREE_SORTABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_TREE_SORTABLE, GtkTreeSortable)) +#define GTK_IS_TREE_SORTABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_TREE_SORTABLE)) +#define GTK_TREE_SORTABLE_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GTK_TYPE_TREE_SORTABLE, GtkTreeSortableIface)) + +/** + * GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID: + * + * Uses the default sort function in a [iface@Gtk.TreeSortable]. + * + * See also: [method@Gtk.TreeSortable.set_sort_column_id] + */ +#define GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID (-1) + +/** + * GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID: + * + * Disables sorting in a [iface@Gtk.TreeSortable]. + * + * See also: [method@Gtk.TreeSortable.set_sort_column_id] + */ +#define GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID (-2) + +typedef struct _GtkTreeSortable GtkTreeSortable; /* Dummy typedef */ +typedef struct _GtkTreeSortableIface GtkTreeSortableIface; + +/** + * GtkTreeIterCompareFunc: + * @model: The `GtkTreeModel` the comparison is within + * @a: A `GtkTreeIter` in @model + * @b: Another `GtkTreeIter` in @model + * @user_data: Data passed when the compare func is assigned e.g. by + * gtk_tree_sortable_set_sort_func() + * + * A GtkTreeIterCompareFunc should return a negative integer, zero, or a positive + * integer if @a sorts before @b, @a sorts with @b, or @a sorts after @b + * respectively. + * + * If two iters compare as equal, their order in the sorted model + * is undefined. In order to ensure that the `GtkTreeSortable` behaves as + * expected, the GtkTreeIterCompareFunc must define a partial order on + * the model, i.e. it must be reflexive, antisymmetric and transitive. + * + * For example, if @model is a product catalogue, then a compare function + * for the “price” column could be one which returns + * `price_of(@a) - price_of(@b)`. + * + * Returns: a negative integer, zero or a positive integer depending on whether + * @a sorts before, with or after @b + */ +typedef int (* GtkTreeIterCompareFunc) (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data); + + +/** + * GtkTreeSortableIface: + * @sort_column_changed: Signal emitted when the sort column or sort + * order of sortable is changed. + * @get_sort_column_id: Fills in sort_column_id and order with the + * current sort column and the order. + * @set_sort_column_id: Sets the current sort column to be + * sort_column_id. + * @set_sort_func: Sets the comparison function used when sorting to + * be sort_func. + * @set_default_sort_func: Sets the default comparison function used + * when sorting to be sort_func. + * @has_default_sort_func: %TRUE if the model has a default sort + * function. + */ +struct _GtkTreeSortableIface +{ + /*< private >*/ + GTypeInterface g_iface; + + /*< public >*/ + + /* signals */ + void (* sort_column_changed) (GtkTreeSortable *sortable); + + /* virtual table */ + gboolean (* get_sort_column_id) (GtkTreeSortable *sortable, + int *sort_column_id, + GtkSortType *order); + void (* set_sort_column_id) (GtkTreeSortable *sortable, + int sort_column_id, + GtkSortType order); + void (* set_sort_func) (GtkTreeSortable *sortable, + int sort_column_id, + GtkTreeIterCompareFunc sort_func, + gpointer user_data, + GDestroyNotify destroy); + void (* set_default_sort_func) (GtkTreeSortable *sortable, + GtkTreeIterCompareFunc sort_func, + gpointer user_data, + GDestroyNotify destroy); + gboolean (* has_default_sort_func) (GtkTreeSortable *sortable); +}; + + +GDK_AVAILABLE_IN_ALL +GType gtk_tree_sortable_get_type (void) G_GNUC_CONST; + +GDK_DEPRECATED_IN_4_10 +void gtk_tree_sortable_sort_column_changed (GtkTreeSortable *sortable); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_sortable_get_sort_column_id (GtkTreeSortable *sortable, + int *sort_column_id, + GtkSortType *order); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_sortable_set_sort_column_id (GtkTreeSortable *sortable, + int sort_column_id, + GtkSortType order); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_sortable_set_sort_func (GtkTreeSortable *sortable, + int sort_column_id, + GtkTreeIterCompareFunc sort_func, + gpointer user_data, + GDestroyNotify destroy); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_sortable_set_default_sort_func (GtkTreeSortable *sortable, + GtkTreeIterCompareFunc sort_func, + gpointer user_data, + GDestroyNotify destroy); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_sortable_has_default_sort_func (GtkTreeSortable *sortable); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkTreeSortable, g_object_unref) + +G_END_DECLS + +#endif /* __GTK_TREE_SORTABLE_H__ */ diff --git a/gtk/deprecated/gtktreestore.c b/gtk/deprecated/gtktreestore.c new file mode 100644 index 0000000000..fc946fe360 --- /dev/null +++ b/gtk/deprecated/gtktreestore.c @@ -0,0 +1,3741 @@ +/* gtktreestore.c + * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#include "config.h" +#include +#include +#include "gtktreemodel.h" +#include "gtktreestore.h" +#include "gtktreedatalistprivate.h" +#include "gtktreednd.h" +#include "gtkbuildable.h" +#include "gtkbuilderprivate.h" +#include "gtkdebug.h" + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + +/** + * GtkTreeStore: + * + * A tree-like data structure that can be used with the GtkTreeView + * + * The `GtkTreeStore` object is a list model for use with a `GtkTreeView` + * widget. It implements the `GtkTreeModel` interface, and consequently, + * can use all of the methods available there. It also implements the + * `GtkTreeSortable` interface so it can be sorted by the view. Finally, + * it also implements the tree + * [drag and drop][gtk3-GtkTreeView-drag-and-drop] + * interfaces. + * + * # GtkTreeStore as GtkBuildable + * + * The GtkTreeStore implementation of the `GtkBuildable` interface allows + * to specify the model columns with a element that may contain + * multiple elements, each specifying one model column. The “type” + * attribute specifies the data type for the column. + * + * An example of a UI Definition fragment for a tree store: + * |[ + * + * + * + * + * + * + * + * ]| + */ + +struct _GtkTreeStorePrivate +{ + int stamp; + GtkSortType order; + gpointer root; + gpointer last; + int n_columns; + int sort_column_id; + GList *sort_list; + GType *column_headers; + GtkTreeIterCompareFunc default_sort_func; + gpointer default_sort_data; + GDestroyNotify default_sort_destroy; + guint columns_dirty : 1; +}; + + +#define G_NODE(node) ((GNode *)node) +#define GTK_TREE_STORE_IS_SORTED(tree) (((GtkTreeStore*)(tree))->priv->sort_column_id != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID) +#define VALID_ITER(iter, tree_store) ((iter)!= NULL && (iter)->user_data != NULL && ((GtkTreeStore*)(tree_store))->priv->stamp == (iter)->stamp) + +static void gtk_tree_store_tree_model_init (GtkTreeModelIface *iface); +static void gtk_tree_store_drag_source_init(GtkTreeDragSourceIface *iface); +static void gtk_tree_store_drag_dest_init (GtkTreeDragDestIface *iface); +static void gtk_tree_store_sortable_init (GtkTreeSortableIface *iface); +static void gtk_tree_store_buildable_init (GtkBuildableIface *iface); +static void gtk_tree_store_finalize (GObject *object); +static GtkTreeModelFlags gtk_tree_store_get_flags (GtkTreeModel *tree_model); +static int gtk_tree_store_get_n_columns (GtkTreeModel *tree_model); +static GType gtk_tree_store_get_column_type (GtkTreeModel *tree_model, + int index); +static gboolean gtk_tree_store_get_iter (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreePath *path); +static GtkTreePath *gtk_tree_store_get_path (GtkTreeModel *tree_model, + GtkTreeIter *iter); +static void gtk_tree_store_get_value (GtkTreeModel *tree_model, + GtkTreeIter *iter, + int column, + GValue *value); +static gboolean gtk_tree_store_iter_next (GtkTreeModel *tree_model, + GtkTreeIter *iter); +static gboolean gtk_tree_store_iter_previous (GtkTreeModel *tree_model, + GtkTreeIter *iter); +static gboolean gtk_tree_store_iter_children (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent); +static gboolean gtk_tree_store_iter_has_child (GtkTreeModel *tree_model, + GtkTreeIter *iter); +static int gtk_tree_store_iter_n_children (GtkTreeModel *tree_model, + GtkTreeIter *iter); +static gboolean gtk_tree_store_iter_nth_child (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent, + int n); +static gboolean gtk_tree_store_iter_parent (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *child); + + +static void gtk_tree_store_set_n_columns (GtkTreeStore *tree_store, + int n_columns); +static void gtk_tree_store_set_column_type (GtkTreeStore *tree_store, + int column, + GType type); + +static void gtk_tree_store_increment_stamp (GtkTreeStore *tree_store); + + +/* DND interfaces */ +static gboolean real_gtk_tree_store_row_draggable (GtkTreeDragSource *drag_source, + GtkTreePath *path); +static gboolean gtk_tree_store_drag_data_delete (GtkTreeDragSource *drag_source, + GtkTreePath *path); +static GdkContentProvider * + gtk_tree_store_drag_data_get (GtkTreeDragSource *drag_source, + GtkTreePath *path); +static gboolean gtk_tree_store_drag_data_received (GtkTreeDragDest *drag_dest, + GtkTreePath *dest, + const GValue *value); +static gboolean gtk_tree_store_row_drop_possible (GtkTreeDragDest *drag_dest, + GtkTreePath *dest_path, + const GValue *value); + +/* Sortable Interfaces */ + +static void gtk_tree_store_sort (GtkTreeStore *tree_store); +static void gtk_tree_store_sort_iter_changed (GtkTreeStore *tree_store, + GtkTreeIter *iter, + int column, + gboolean emit_signal); +static gboolean gtk_tree_store_get_sort_column_id (GtkTreeSortable *sortable, + int *sort_column_id, + GtkSortType *order); +static void gtk_tree_store_set_sort_column_id (GtkTreeSortable *sortable, + int sort_column_id, + GtkSortType order); +static void gtk_tree_store_set_sort_func (GtkTreeSortable *sortable, + int sort_column_id, + GtkTreeIterCompareFunc func, + gpointer data, + GDestroyNotify destroy); +static void gtk_tree_store_set_default_sort_func (GtkTreeSortable *sortable, + GtkTreeIterCompareFunc func, + gpointer data, + GDestroyNotify destroy); +static gboolean gtk_tree_store_has_default_sort_func (GtkTreeSortable *sortable); + + +/* buildable */ + +static gboolean gtk_tree_store_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const char *tagname, + GtkBuildableParser *parser, + gpointer *data); +static void gtk_tree_store_buildable_custom_tag_end (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const char *tagname, + gpointer parser_data); + +static void gtk_tree_store_move (GtkTreeStore *tree_store, + GtkTreeIter *iter, + GtkTreeIter *position, + gboolean before); + + +#ifdef G_ENABLE_DEBUG +static void validate_gnode (GNode *node); + +static inline void +validate_tree (GtkTreeStore *tree_store) +{ + if (GTK_DEBUG_CHECK (TREE)) + { + g_assert (G_NODE (tree_store->priv->root)->parent == NULL); + validate_gnode (G_NODE (tree_store->priv->root)); + } +} +#else +#define validate_tree(store) +#endif + +G_DEFINE_TYPE_WITH_CODE (GtkTreeStore, gtk_tree_store, G_TYPE_OBJECT, + G_ADD_PRIVATE (GtkTreeStore) + G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, + gtk_tree_store_tree_model_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE, + gtk_tree_store_drag_source_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_DEST, + gtk_tree_store_drag_dest_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_SORTABLE, + gtk_tree_store_sortable_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, + gtk_tree_store_buildable_init)) + +static void +gtk_tree_store_class_init (GtkTreeStoreClass *class) +{ + GObjectClass *object_class; + + object_class = (GObjectClass *) class; + + object_class->finalize = gtk_tree_store_finalize; +} + +static void +gtk_tree_store_tree_model_init (GtkTreeModelIface *iface) +{ + iface->get_flags = gtk_tree_store_get_flags; + iface->get_n_columns = gtk_tree_store_get_n_columns; + iface->get_column_type = gtk_tree_store_get_column_type; + iface->get_iter = gtk_tree_store_get_iter; + iface->get_path = gtk_tree_store_get_path; + iface->get_value = gtk_tree_store_get_value; + iface->iter_next = gtk_tree_store_iter_next; + iface->iter_previous = gtk_tree_store_iter_previous; + iface->iter_children = gtk_tree_store_iter_children; + iface->iter_has_child = gtk_tree_store_iter_has_child; + iface->iter_n_children = gtk_tree_store_iter_n_children; + iface->iter_nth_child = gtk_tree_store_iter_nth_child; + iface->iter_parent = gtk_tree_store_iter_parent; +} + +static void +gtk_tree_store_drag_source_init (GtkTreeDragSourceIface *iface) +{ + iface->row_draggable = real_gtk_tree_store_row_draggable; + iface->drag_data_delete = gtk_tree_store_drag_data_delete; + iface->drag_data_get = gtk_tree_store_drag_data_get; +} + +static void +gtk_tree_store_drag_dest_init (GtkTreeDragDestIface *iface) +{ + iface->drag_data_received = gtk_tree_store_drag_data_received; + iface->row_drop_possible = gtk_tree_store_row_drop_possible; +} + +static void +gtk_tree_store_sortable_init (GtkTreeSortableIface *iface) +{ + iface->get_sort_column_id = gtk_tree_store_get_sort_column_id; + iface->set_sort_column_id = gtk_tree_store_set_sort_column_id; + iface->set_sort_func = gtk_tree_store_set_sort_func; + iface->set_default_sort_func = gtk_tree_store_set_default_sort_func; + iface->has_default_sort_func = gtk_tree_store_has_default_sort_func; +} + +void +gtk_tree_store_buildable_init (GtkBuildableIface *iface) +{ + iface->custom_tag_start = gtk_tree_store_buildable_custom_tag_start; + iface->custom_tag_end = gtk_tree_store_buildable_custom_tag_end; +} + +static void +gtk_tree_store_init (GtkTreeStore *tree_store) +{ + GtkTreeStorePrivate *priv; + + priv = gtk_tree_store_get_instance_private (tree_store); + tree_store->priv = priv; + priv->root = g_node_new (NULL); + /* While the odds are against us getting 0... */ + do + { + priv->stamp = g_random_int (); + } + while (priv->stamp == 0); + + priv->sort_list = NULL; + priv->sort_column_id = GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID; + priv->columns_dirty = FALSE; +} + +/** + * gtk_tree_store_new: + * @n_columns: number of columns in the tree store + * @...: all `GType` types for the columns, from first to last + * + * Creates a new tree store as with @n_columns columns each of the types passed + * in. Note that only types derived from standard GObject fundamental types + * are supported. + * + * As an example, + * + * ``` + * gtk_tree_store_new (3, G_TYPE_INT, G_TYPE_STRING, GDK_TYPE_TEXTURE); + * ``` + * + * will create a new `GtkTreeStore` with three columns, of type + * `int`, `gchararray`, and `GdkTexture` respectively. + * + * Returns: a new `GtkTreeStore` + * + * Deprecated: 4.10 + **/ +GtkTreeStore * +gtk_tree_store_new (int n_columns, + ...) +{ + GtkTreeStore *retval; + va_list args; + int i; + + g_return_val_if_fail (n_columns > 0, NULL); + + retval = g_object_new (GTK_TYPE_TREE_STORE, NULL); + gtk_tree_store_set_n_columns (retval, n_columns); + + va_start (args, n_columns); + + for (i = 0; i < n_columns; i++) + { + GType type = va_arg (args, GType); + if (! _gtk_tree_data_list_check_type (type)) + { + g_warning ("%s: Invalid type %s", G_STRLOC, g_type_name (type)); + g_object_unref (retval); + va_end (args); + return NULL; + } + gtk_tree_store_set_column_type (retval, i, type); + } + va_end (args); + + return retval; +} +/** + * gtk_tree_store_newv: (rename-to gtk_tree_store_new) + * @n_columns: number of columns in the tree store + * @types: (array length=n_columns): an array of `GType` types for the columns, from first to last + * + * Non vararg creation function. Used primarily by language bindings. + * + * Returns: (transfer full): a new `GtkTreeStore` + * + * Deprecated: 4.10 + **/ +GtkTreeStore * +gtk_tree_store_newv (int n_columns, + GType *types) +{ + GtkTreeStore *retval; + int i; + + g_return_val_if_fail (n_columns > 0, NULL); + + retval = g_object_new (GTK_TYPE_TREE_STORE, NULL); + gtk_tree_store_set_n_columns (retval, n_columns); + + for (i = 0; i < n_columns; i++) + { + if (! _gtk_tree_data_list_check_type (types[i])) + { + g_warning ("%s: Invalid type %s", G_STRLOC, g_type_name (types[i])); + g_object_unref (retval); + return NULL; + } + gtk_tree_store_set_column_type (retval, i, types[i]); + } + + return retval; +} + + +/** + * gtk_tree_store_set_column_types: + * @tree_store: A `GtkTreeStore` + * @n_columns: Number of columns for the tree store + * @types: (array length=n_columns): An array of `GType` types, one for each column + * + * This function is meant primarily for `GObjects` that inherit from + * `GtkTreeStore`, and should only be used when constructing a new + * `GtkTreeStore`. It will not function after a row has been added, + * or a method on the `GtkTreeModel` interface is called. + * + * Deprecated: 4.10 + **/ +void +gtk_tree_store_set_column_types (GtkTreeStore *tree_store, + int n_columns, + GType *types) +{ + int i; + + g_return_if_fail (GTK_IS_TREE_STORE (tree_store)); + g_return_if_fail (tree_store->priv->columns_dirty == 0); + + gtk_tree_store_set_n_columns (tree_store, n_columns); + for (i = 0; i < n_columns; i++) + { + if (! _gtk_tree_data_list_check_type (types[i])) + { + g_warning ("%s: Invalid type %s", G_STRLOC, g_type_name (types[i])); + continue; + } + gtk_tree_store_set_column_type (tree_store, i, types[i]); + } +} + +static void +gtk_tree_store_set_n_columns (GtkTreeStore *tree_store, + int n_columns) +{ + GtkTreeStorePrivate *priv = tree_store->priv; + int i; + + if (priv->n_columns == n_columns) + return; + + priv->column_headers = g_renew (GType, priv->column_headers, n_columns); + for (i = priv->n_columns; i < n_columns; i++) + priv->column_headers[i] = G_TYPE_INVALID; + priv->n_columns = n_columns; + + if (priv->sort_list) + _gtk_tree_data_list_header_free (priv->sort_list); + + priv->sort_list = _gtk_tree_data_list_header_new (n_columns, priv->column_headers); +} + +/** + * gtk_tree_store_set_column_type: + * @tree_store: a `GtkTreeStore` + * @column: column number + * @type: type of the data to be stored in @column + * + * Supported types include: %G_TYPE_UINT, %G_TYPE_INT, %G_TYPE_UCHAR, + * %G_TYPE_CHAR, %G_TYPE_BOOLEAN, %G_TYPE_POINTER, %G_TYPE_FLOAT, + * %G_TYPE_DOUBLE, %G_TYPE_STRING, %G_TYPE_OBJECT, and %G_TYPE_BOXED, along with + * subclasses of those types such as %GDK_TYPE_PIXBUF. + * + * Deprecated: 4.10 + **/ +static void +gtk_tree_store_set_column_type (GtkTreeStore *tree_store, + int column, + GType type) +{ + GtkTreeStorePrivate *priv = tree_store->priv; + + if (!_gtk_tree_data_list_check_type (type)) + { + g_warning ("%s: Invalid type %s", G_STRLOC, g_type_name (type)); + return; + } + priv->column_headers[column] = type; +} + +static gboolean +node_free (GNode *node, gpointer data) +{ + if (node->data) + _gtk_tree_data_list_free (node->data, (GType*)data); + node->data = NULL; + + return FALSE; +} + +static void +gtk_tree_store_finalize (GObject *object) +{ + GtkTreeStore *tree_store = GTK_TREE_STORE (object); + GtkTreeStorePrivate *priv = tree_store->priv; + + g_node_traverse (priv->root, G_POST_ORDER, G_TRAVERSE_ALL, -1, + node_free, priv->column_headers); + g_node_destroy (priv->root); + _gtk_tree_data_list_header_free (priv->sort_list); + g_free (priv->column_headers); + + if (priv->default_sort_destroy) + { + GDestroyNotify d = priv->default_sort_destroy; + + priv->default_sort_destroy = NULL; + d (priv->default_sort_data); + priv->default_sort_data = NULL; + } + + /* must chain up */ + G_OBJECT_CLASS (gtk_tree_store_parent_class)->finalize (object); +} + +/* fulfill the GtkTreeModel requirements */ +/* NOTE: GtkTreeStore::root is a GNode, that acts as the parent node. However, + * it is not visible to the tree or to the user., and the path “0” refers to the + * first child of GtkTreeStore::root. + */ + + +static GtkTreeModelFlags +gtk_tree_store_get_flags (GtkTreeModel *tree_model) +{ + return GTK_TREE_MODEL_ITERS_PERSIST; +} + +static int +gtk_tree_store_get_n_columns (GtkTreeModel *tree_model) +{ + GtkTreeStore *tree_store = (GtkTreeStore *) tree_model; + GtkTreeStorePrivate *priv = tree_store->priv; + + priv->columns_dirty = TRUE; + + return priv->n_columns; +} + +static GType +gtk_tree_store_get_column_type (GtkTreeModel *tree_model, + int index) +{ + GtkTreeStore *tree_store = (GtkTreeStore *) tree_model; + GtkTreeStorePrivate *priv = tree_store->priv; + + g_return_val_if_fail (index < priv->n_columns, G_TYPE_INVALID); + + priv->columns_dirty = TRUE; + + return priv->column_headers[index]; +} + +static gboolean +gtk_tree_store_get_iter (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreePath *path) +{ + GtkTreeStore *tree_store = (GtkTreeStore *) tree_model; + GtkTreeStorePrivate *priv = tree_store->priv; + GtkTreeIter parent; + int *indices; + int depth, i; + + priv->columns_dirty = TRUE; + + indices = gtk_tree_path_get_indices (path); + depth = gtk_tree_path_get_depth (path); + + g_return_val_if_fail (depth > 0, FALSE); + + parent.stamp = priv->stamp; + parent.user_data = priv->root; + + if (!gtk_tree_store_iter_nth_child (tree_model, iter, &parent, indices[0])) + { + iter->stamp = 0; + return FALSE; + } + + for (i = 1; i < depth; i++) + { + parent = *iter; + if (!gtk_tree_store_iter_nth_child (tree_model, iter, &parent, indices[i])) + { + iter->stamp = 0; + return FALSE; + } + } + + return TRUE; +} + +static GtkTreePath * +gtk_tree_store_get_path (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + GtkTreeStore *tree_store = (GtkTreeStore *) tree_model; + GtkTreeStorePrivate *priv = tree_store->priv; + GtkTreePath *retval; + GNode *tmp_node; + int i = 0; + + g_return_val_if_fail (iter->user_data != NULL, NULL); + g_return_val_if_fail (iter->stamp == priv->stamp, NULL); + + validate_tree (tree_store); + + if (G_NODE (iter->user_data)->parent == NULL && + G_NODE (iter->user_data) == priv->root) + return gtk_tree_path_new (); + g_assert (G_NODE (iter->user_data)->parent != NULL); + + if (G_NODE (iter->user_data)->parent == G_NODE (priv->root)) + { + retval = gtk_tree_path_new (); + tmp_node = G_NODE (priv->root)->children; + } + else + { + GtkTreeIter tmp_iter = *iter; + + tmp_iter.user_data = G_NODE (iter->user_data)->parent; + + retval = gtk_tree_store_get_path (tree_model, &tmp_iter); + tmp_node = G_NODE (iter->user_data)->parent->children; + } + + if (retval == NULL) + return NULL; + + if (tmp_node == NULL) + { + gtk_tree_path_free (retval); + return NULL; + } + + for (; tmp_node; tmp_node = tmp_node->next) + { + if (tmp_node == G_NODE (iter->user_data)) + break; + i++; + } + + if (tmp_node == NULL) + { + /* We couldn't find node, meaning it's prolly not ours */ + /* Perhaps I should do a g_return_if_fail here. */ + gtk_tree_path_free (retval); + return NULL; + } + + gtk_tree_path_append_index (retval, i); + + return retval; +} + + +static void +gtk_tree_store_get_value (GtkTreeModel *tree_model, + GtkTreeIter *iter, + int column, + GValue *value) +{ + GtkTreeStore *tree_store = (GtkTreeStore *) tree_model; + GtkTreeStorePrivate *priv = tree_store->priv; + GtkTreeDataList *list; + int tmp_column = column; + + g_return_if_fail (column < priv->n_columns); + g_return_if_fail (VALID_ITER (iter, tree_store)); + + list = G_NODE (iter->user_data)->data; + + while (tmp_column-- > 0 && list) + list = list->next; + + if (list) + { + _gtk_tree_data_list_node_to_value (list, + priv->column_headers[column], + value); + } + else + { + /* We want to return an initialized but empty (default) value */ + g_value_init (value, priv->column_headers[column]); + } +} + +static gboolean +gtk_tree_store_iter_next (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + g_return_val_if_fail (iter->user_data != NULL, FALSE); + g_return_val_if_fail (iter->stamp == GTK_TREE_STORE (tree_model)->priv->stamp, FALSE); + + if (G_NODE (iter->user_data)->next == NULL) + { + iter->stamp = 0; + return FALSE; + } + + iter->user_data = G_NODE (iter->user_data)->next; + + return TRUE; +} + +static gboolean +gtk_tree_store_iter_previous (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + g_return_val_if_fail (iter->user_data != NULL, FALSE); + g_return_val_if_fail (iter->stamp == GTK_TREE_STORE (tree_model)->priv->stamp, FALSE); + + if (G_NODE (iter->user_data)->prev == NULL) + { + iter->stamp = 0; + return FALSE; + } + + iter->user_data = G_NODE (iter->user_data)->prev; + + return TRUE; +} + +static gboolean +gtk_tree_store_iter_children (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent) +{ + GtkTreeStore *tree_store = (GtkTreeStore *) tree_model; + GtkTreeStorePrivate *priv = tree_store->priv; + GNode *children; + + if (parent) + g_return_val_if_fail (VALID_ITER (parent, tree_store), FALSE); + + if (parent) + children = G_NODE (parent->user_data)->children; + else + children = G_NODE (priv->root)->children; + + if (children) + { + iter->stamp = priv->stamp; + iter->user_data = children; + return TRUE; + } + else + { + iter->stamp = 0; + return FALSE; + } +} + +static gboolean +gtk_tree_store_iter_has_child (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + g_return_val_if_fail (iter->user_data != NULL, FALSE); + g_return_val_if_fail (VALID_ITER (iter, tree_model), FALSE); + + return G_NODE (iter->user_data)->children != NULL; +} + +static int +gtk_tree_store_iter_n_children (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + GNode *node; + int i = 0; + + g_return_val_if_fail (iter == NULL || iter->user_data != NULL, 0); + + if (iter == NULL) + node = G_NODE (GTK_TREE_STORE (tree_model)->priv->root)->children; + else + node = G_NODE (iter->user_data)->children; + + while (node) + { + i++; + node = node->next; + } + + return i; +} + +static gboolean +gtk_tree_store_iter_nth_child (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent, + int n) +{ + GtkTreeStore *tree_store = (GtkTreeStore *) tree_model; + GtkTreeStorePrivate *priv = tree_store->priv; + GNode *parent_node; + GNode *child; + + g_return_val_if_fail (parent == NULL || parent->user_data != NULL, FALSE); + + if (parent == NULL) + parent_node = priv->root; + else + parent_node = parent->user_data; + + child = g_node_nth_child (parent_node, n); + + if (child) + { + iter->user_data = child; + iter->stamp = priv->stamp; + return TRUE; + } + else + { + iter->stamp = 0; + return FALSE; + } +} + +static gboolean +gtk_tree_store_iter_parent (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *child) +{ + GtkTreeStore *tree_store = (GtkTreeStore *) tree_model; + GtkTreeStorePrivate *priv = tree_store->priv; + GNode *parent; + + g_return_val_if_fail (iter != NULL, FALSE); + g_return_val_if_fail (VALID_ITER (child, tree_store), FALSE); + + parent = G_NODE (child->user_data)->parent; + + g_assert (parent != NULL); + + if (parent != priv->root) + { + iter->user_data = parent; + iter->stamp = priv->stamp; + return TRUE; + } + else + { + iter->stamp = 0; + return FALSE; + } +} + + +/* Does not emit a signal */ +static gboolean +gtk_tree_store_real_set_value (GtkTreeStore *tree_store, + GtkTreeIter *iter, + int column, + GValue *value, + gboolean sort) +{ + GtkTreeStorePrivate *priv = tree_store->priv; + GtkTreeDataList *list; + GtkTreeDataList *prev; + int old_column = column; + GValue real_value = G_VALUE_INIT; + gboolean converted = FALSE; + gboolean retval = FALSE; + + if (! g_type_is_a (G_VALUE_TYPE (value), priv->column_headers[column])) + { + if (! (g_value_type_transformable (G_VALUE_TYPE (value), priv->column_headers[column]))) + { + g_warning ("%s: Unable to convert from %s to %s", + G_STRLOC, + g_type_name (G_VALUE_TYPE (value)), + g_type_name (priv->column_headers[column])); + return retval; + } + + g_value_init (&real_value, priv->column_headers[column]); + if (!g_value_transform (value, &real_value)) + { + g_warning ("%s: Unable to make conversion from %s to %s", + G_STRLOC, + g_type_name (G_VALUE_TYPE (value)), + g_type_name (priv->column_headers[column])); + g_value_unset (&real_value); + return retval; + } + converted = TRUE; + } + + prev = list = G_NODE (iter->user_data)->data; + + while (list != NULL) + { + if (column == 0) + { + if (converted) + _gtk_tree_data_list_value_to_node (list, &real_value); + else + _gtk_tree_data_list_value_to_node (list, value); + retval = TRUE; + if (converted) + g_value_unset (&real_value); + if (sort && GTK_TREE_STORE_IS_SORTED (tree_store)) + gtk_tree_store_sort_iter_changed (tree_store, iter, old_column, TRUE); + return retval; + } + + column--; + prev = list; + list = list->next; + } + + if (G_NODE (iter->user_data)->data == NULL) + { + G_NODE (iter->user_data)->data = list = _gtk_tree_data_list_alloc (); + list->next = NULL; + } + else + { + list = prev->next = _gtk_tree_data_list_alloc (); + list->next = NULL; + } + + while (column != 0) + { + list->next = _gtk_tree_data_list_alloc (); + list = list->next; + list->next = NULL; + column --; + } + + if (converted) + _gtk_tree_data_list_value_to_node (list, &real_value); + else + _gtk_tree_data_list_value_to_node (list, value); + + retval = TRUE; + if (converted) + g_value_unset (&real_value); + + if (sort && GTK_TREE_STORE_IS_SORTED (tree_store)) + gtk_tree_store_sort_iter_changed (tree_store, iter, old_column, TRUE); + + return retval; +} + +/** + * gtk_tree_store_set_value: + * @tree_store: a `GtkTreeStore` + * @iter: A valid `GtkTreeIter` for the row being modified + * @column: column number to modify + * @value: new value for the cell + * + * Sets the data in the cell specified by @iter and @column. + * The type of @value must be convertible to the type of the + * column. + * + * Deprecated: 4.10 + **/ +void +gtk_tree_store_set_value (GtkTreeStore *tree_store, + GtkTreeIter *iter, + int column, + GValue *value) +{ + g_return_if_fail (GTK_IS_TREE_STORE (tree_store)); + g_return_if_fail (VALID_ITER (iter, tree_store)); + g_return_if_fail (column >= 0 && column < tree_store->priv->n_columns); + g_return_if_fail (G_IS_VALUE (value)); + + if (gtk_tree_store_real_set_value (tree_store, iter, column, value, TRUE)) + { + GtkTreePath *path; + + path = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter); + gtk_tree_model_row_changed (GTK_TREE_MODEL (tree_store), path, iter); + gtk_tree_path_free (path); + } +} + +static GtkTreeIterCompareFunc +gtk_tree_store_get_compare_func (GtkTreeStore *tree_store) +{ + GtkTreeStorePrivate *priv = tree_store->priv; + GtkTreeIterCompareFunc func = NULL; + + if (GTK_TREE_STORE_IS_SORTED (tree_store)) + { + if (priv->sort_column_id != -1) + { + GtkTreeDataSortHeader *header; + header = _gtk_tree_data_list_get_header (priv->sort_list, + priv->sort_column_id); + g_return_val_if_fail (header != NULL, NULL); + g_return_val_if_fail (header->func != NULL, NULL); + func = header->func; + } + else + { + func = priv->default_sort_func; + } + } + + return func; +} + +static void +gtk_tree_store_set_vector_internal (GtkTreeStore *tree_store, + GtkTreeIter *iter, + gboolean *emit_signal, + gboolean *maybe_need_sort, + int *columns, + GValue *values, + int n_values) +{ + GtkTreeStorePrivate *priv = tree_store->priv; + int i; + GtkTreeIterCompareFunc func = NULL; + + func = gtk_tree_store_get_compare_func (tree_store); + if (func != _gtk_tree_data_list_compare_func) + *maybe_need_sort = TRUE; + + for (i = 0; i < n_values; i++) + { + *emit_signal = gtk_tree_store_real_set_value (tree_store, iter, + columns[i], &values[i], + FALSE) || *emit_signal; + + if (func == _gtk_tree_data_list_compare_func && + columns[i] == priv->sort_column_id) + *maybe_need_sort = TRUE; + } +} + +static void +gtk_tree_store_set_valist_internal (GtkTreeStore *tree_store, + GtkTreeIter *iter, + gboolean *emit_signal, + gboolean *maybe_need_sort, + va_list var_args) +{ + GtkTreeStorePrivate *priv = tree_store->priv; + int column; + GtkTreeIterCompareFunc func = NULL; + + column = va_arg (var_args, int); + + func = gtk_tree_store_get_compare_func (tree_store); + if (func != _gtk_tree_data_list_compare_func) + *maybe_need_sort = TRUE; + + while (column != -1) + { + GValue value = G_VALUE_INIT; + char *error = NULL; + + if (column < 0 || column >= priv->n_columns) + { + g_warning ("%s: Invalid column number %d added to iter (remember to end your list of columns with a -1)", G_STRLOC, column); + break; + } + + G_VALUE_COLLECT_INIT (&value, priv->column_headers[column], + var_args, 0, &error); + if (error) + { + g_warning ("%s: %s", G_STRLOC, error); + g_free (error); + + /* we purposely leak the value here, it might not be + * in a sane state if an error condition occurred + */ + break; + } + + *emit_signal = gtk_tree_store_real_set_value (tree_store, + iter, + column, + &value, + FALSE) || *emit_signal; + + if (func == _gtk_tree_data_list_compare_func && + column == priv->sort_column_id) + *maybe_need_sort = TRUE; + + g_value_unset (&value); + + column = va_arg (var_args, int); + } +} + +/** + * gtk_tree_store_set_valuesv: (rename-to gtk_tree_store_set) + * @tree_store: A `GtkTreeStore` + * @iter: A valid `GtkTreeIter` for the row being modified + * @columns: (array length=n_values): an array of column numbers + * @values: (array length=n_values): an array of GValues + * @n_values: the length of the @columns and @values arrays + * + * A variant of gtk_tree_store_set_valist() which takes + * the columns and values as two arrays, instead of varargs. This + * function is mainly intended for language bindings or in case + * the number of columns to change is not known until run-time. + * + * Deprecated: 4.10 + **/ +void +gtk_tree_store_set_valuesv (GtkTreeStore *tree_store, + GtkTreeIter *iter, + int *columns, + GValue *values, + int n_values) +{ + GtkTreeStorePrivate *priv = tree_store->priv; + gboolean emit_signal = FALSE; + gboolean maybe_need_sort = FALSE; + + g_return_if_fail (GTK_IS_TREE_STORE (tree_store)); + g_return_if_fail (VALID_ITER (iter, tree_store)); + + gtk_tree_store_set_vector_internal (tree_store, iter, + &emit_signal, + &maybe_need_sort, + columns, values, n_values); + + if (maybe_need_sort && GTK_TREE_STORE_IS_SORTED (tree_store)) + gtk_tree_store_sort_iter_changed (tree_store, iter, priv->sort_column_id, TRUE); + + if (emit_signal) + { + GtkTreePath *path; + + path = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter); + gtk_tree_model_row_changed (GTK_TREE_MODEL (tree_store), path, iter); + gtk_tree_path_free (path); + } +} + +/** + * gtk_tree_store_set_valist: + * @tree_store: A `GtkTreeStore` + * @iter: A valid `GtkTreeIter` for the row being modified + * @var_args: va_list of column/value pairs + * + * See gtk_tree_store_set(); this version takes a va_list for + * use by language bindings. + * + * Deprecated: 4.10 + **/ +void +gtk_tree_store_set_valist (GtkTreeStore *tree_store, + GtkTreeIter *iter, + va_list var_args) +{ + GtkTreeStorePrivate *priv = tree_store->priv; + gboolean emit_signal = FALSE; + gboolean maybe_need_sort = FALSE; + + g_return_if_fail (GTK_IS_TREE_STORE (tree_store)); + g_return_if_fail (VALID_ITER (iter, tree_store)); + + gtk_tree_store_set_valist_internal (tree_store, iter, + &emit_signal, + &maybe_need_sort, + var_args); + + if (maybe_need_sort && GTK_TREE_STORE_IS_SORTED (tree_store)) + gtk_tree_store_sort_iter_changed (tree_store, iter, priv->sort_column_id, TRUE); + + if (emit_signal) + { + GtkTreePath *path; + + path = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter); + gtk_tree_model_row_changed (GTK_TREE_MODEL (tree_store), path, iter); + gtk_tree_path_free (path); + } +} + +/** + * gtk_tree_store_set: + * @tree_store: A `GtkTreeStore` + * @iter: A valid `GtkTreeIter` for the row being modified + * @...: pairs of column number and value, terminated with -1 + * + * Sets the value of one or more cells in the row referenced by @iter. + * The variable argument list should contain integer column numbers, + * each column number followed by the value to be set. + * The list is terminated by a -1. For example, to set column 0 with type + * %G_TYPE_STRING to “Foo”, you would write + * `gtk_tree_store_set (store, iter, 0, "Foo", -1)`. + * + * The value will be referenced by the store if it is a %G_TYPE_OBJECT, and it + * will be copied if it is a %G_TYPE_STRING or %G_TYPE_BOXED. + * + * Deprecated: 4.10 + **/ +void +gtk_tree_store_set (GtkTreeStore *tree_store, + GtkTreeIter *iter, + ...) +{ + va_list var_args; + + va_start (var_args, iter); + gtk_tree_store_set_valist (tree_store, iter, var_args); + va_end (var_args); +} + +/** + * gtk_tree_store_remove: + * @tree_store: A `GtkTreeStore` + * @iter: A valid `GtkTreeIter` + * + * Removes @iter from @tree_store. After being removed, @iter is set to the + * next valid row at that level, or invalidated if it previously pointed to the + * last one. + * + * Returns: %TRUE if @iter is still valid, %FALSE if not. + * + * Deprecated: 4.10 + **/ +gboolean +gtk_tree_store_remove (GtkTreeStore *tree_store, + GtkTreeIter *iter) +{ + GtkTreeStorePrivate *priv = tree_store->priv; + GtkTreePath *path; + GtkTreeIter new_iter = {0,}; + GNode *parent; + GNode *next_node; + + g_return_val_if_fail (GTK_IS_TREE_STORE (tree_store), FALSE); + g_return_val_if_fail (VALID_ITER (iter, tree_store), FALSE); + + parent = G_NODE (iter->user_data)->parent; + + g_assert (parent != NULL); + next_node = G_NODE (iter->user_data)->next; + + if (G_NODE (iter->user_data)->data) + g_node_traverse (G_NODE (iter->user_data), G_POST_ORDER, G_TRAVERSE_ALL, + -1, node_free, priv->column_headers); + + path = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter); + g_node_destroy (G_NODE (iter->user_data)); + + gtk_tree_model_row_deleted (GTK_TREE_MODEL (tree_store), path); + + if (parent != G_NODE (priv->root)) + { + /* child_toggled */ + if (parent->children == NULL) + { + gtk_tree_path_up (path); + + new_iter.stamp = priv->stamp; + new_iter.user_data = parent; + gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (tree_store), path, &new_iter); + } + } + gtk_tree_path_free (path); + + /* revalidate iter */ + if (next_node != NULL) + { + iter->stamp = priv->stamp; + iter->user_data = next_node; + return TRUE; + } + else + { + iter->stamp = 0; + iter->user_data = NULL; + } + + return FALSE; +} + +/** + * gtk_tree_store_insert: + * @tree_store: A `GtkTreeStore` + * @iter: (out): An unset `GtkTreeIter` to set to the new row + * @parent: (nullable): A valid `GtkTreeIter` + * @position: position to insert the new row, or -1 for last + * + * Creates a new row at @position. If parent is non-%NULL, then the row will be + * made a child of @parent. Otherwise, the row will be created at the toplevel. + * If @position is -1 or is larger than the number of rows at that level, then + * the new row will be inserted to the end of the list. @iter will be changed + * to point to this new row. The row will be empty after this function is + * called. To fill in values, you need to call gtk_tree_store_set() or + * gtk_tree_store_set_value(). + * + * Deprecated: 4.10 + **/ +void +gtk_tree_store_insert (GtkTreeStore *tree_store, + GtkTreeIter *iter, + GtkTreeIter *parent, + int position) +{ + GtkTreeStorePrivate *priv = tree_store->priv; + GtkTreePath *path; + GNode *parent_node; + GNode *new_node; + + g_return_if_fail (GTK_IS_TREE_STORE (tree_store)); + g_return_if_fail (iter != NULL); + if (parent) + g_return_if_fail (VALID_ITER (parent, tree_store)); + + if (parent) + parent_node = parent->user_data; + else + parent_node = priv->root; + + priv->columns_dirty = TRUE; + + new_node = g_node_new (NULL); + + iter->stamp = priv->stamp; + iter->user_data = new_node; + g_node_insert (parent_node, position, new_node); + + path = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter); + gtk_tree_model_row_inserted (GTK_TREE_MODEL (tree_store), path, iter); + + if (parent_node != priv->root) + { + if (new_node->prev == NULL && new_node->next == NULL) + { + gtk_tree_path_up (path); + gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (tree_store), path, parent); + } + } + + gtk_tree_path_free (path); + + validate_tree ((GtkTreeStore*)tree_store); +} + +/** + * gtk_tree_store_insert_before: + * @tree_store: A `GtkTreeStore` + * @iter: (out): An unset `GtkTreeIter` to set to the new row + * @parent: (nullable): A valid `GtkTreeIter` + * @sibling: (nullable): A valid `GtkTreeIter` + * + * Inserts a new row before @sibling. If @sibling is %NULL, then the row will + * be appended to @parent ’s children. If @parent and @sibling are %NULL, then + * the row will be appended to the toplevel. If both @sibling and @parent are + * set, then @parent must be the parent of @sibling. When @sibling is set, + * @parent is optional. + * + * @iter will be changed to point to this new row. The row will be empty after + * this function is called. To fill in values, you need to call + * gtk_tree_store_set() or gtk_tree_store_set_value(). + * + * Deprecated: 4.10 + **/ +void +gtk_tree_store_insert_before (GtkTreeStore *tree_store, + GtkTreeIter *iter, + GtkTreeIter *parent, + GtkTreeIter *sibling) +{ + GtkTreeStorePrivate *priv = tree_store->priv; + GtkTreePath *path; + GNode *parent_node = NULL; + GNode *new_node; + + g_return_if_fail (GTK_IS_TREE_STORE (tree_store)); + g_return_if_fail (iter != NULL); + if (parent != NULL) + g_return_if_fail (VALID_ITER (parent, tree_store)); + if (sibling != NULL) + g_return_if_fail (VALID_ITER (sibling, tree_store)); + + if (parent == NULL && sibling == NULL) + parent_node = priv->root; + else if (parent == NULL) + parent_node = G_NODE (sibling->user_data)->parent; + else if (sibling == NULL) + parent_node = G_NODE (parent->user_data); + else + { + g_return_if_fail (G_NODE (sibling->user_data)->parent == G_NODE (parent->user_data)); + parent_node = G_NODE (parent->user_data); + } + + priv->columns_dirty = TRUE; + + new_node = g_node_new (NULL); + + g_node_insert_before (parent_node, + sibling ? G_NODE (sibling->user_data) : NULL, + new_node); + + iter->stamp = priv->stamp; + iter->user_data = new_node; + + path = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter); + gtk_tree_model_row_inserted (GTK_TREE_MODEL (tree_store), path, iter); + + if (parent_node != priv->root) + { + if (new_node->prev == NULL && new_node->next == NULL) + { + GtkTreeIter parent_iter; + + parent_iter.stamp = priv->stamp; + parent_iter.user_data = parent_node; + + gtk_tree_path_up (path); + gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (tree_store), path, &parent_iter); + } + } + + gtk_tree_path_free (path); + + validate_tree (tree_store); +} + +/** + * gtk_tree_store_insert_after: + * @tree_store: A `GtkTreeStore` + * @iter: (out): An unset `GtkTreeIter` to set to the new row + * @parent: (nullable): A valid `GtkTreeIter` + * @sibling: (nullable): A valid `GtkTreeIter` + * + * Inserts a new row after @sibling. If @sibling is %NULL, then the row will be + * prepended to @parent ’s children. If @parent and @sibling are %NULL, then + * the row will be prepended to the toplevel. If both @sibling and @parent are + * set, then @parent must be the parent of @sibling. When @sibling is set, + * @parent is optional. + * + * @iter will be changed to point to this new row. The row will be empty after + * this function is called. To fill in values, you need to call + * gtk_tree_store_set() or gtk_tree_store_set_value(). + * + * Deprecated: 4.10 + **/ +void +gtk_tree_store_insert_after (GtkTreeStore *tree_store, + GtkTreeIter *iter, + GtkTreeIter *parent, + GtkTreeIter *sibling) +{ + GtkTreeStorePrivate *priv = tree_store->priv; + GtkTreePath *path; + GNode *parent_node; + GNode *new_node; + + g_return_if_fail (GTK_IS_TREE_STORE (tree_store)); + g_return_if_fail (iter != NULL); + if (parent != NULL) + g_return_if_fail (VALID_ITER (parent, tree_store)); + if (sibling != NULL) + g_return_if_fail (VALID_ITER (sibling, tree_store)); + + if (parent == NULL && sibling == NULL) + parent_node = priv->root; + else if (parent == NULL) + parent_node = G_NODE (sibling->user_data)->parent; + else if (sibling == NULL) + parent_node = G_NODE (parent->user_data); + else + { + g_return_if_fail (G_NODE (sibling->user_data)->parent == + G_NODE (parent->user_data)); + parent_node = G_NODE (parent->user_data); + } + + priv->columns_dirty = TRUE; + + new_node = g_node_new (NULL); + + g_node_insert_after (parent_node, + sibling ? G_NODE (sibling->user_data) : NULL, + new_node); + + iter->stamp = priv->stamp; + iter->user_data = new_node; + + path = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter); + gtk_tree_model_row_inserted (GTK_TREE_MODEL (tree_store), path, iter); + + if (parent_node != priv->root) + { + if (new_node->prev == NULL && new_node->next == NULL) + { + GtkTreeIter parent_iter; + + parent_iter.stamp = priv->stamp; + parent_iter.user_data = parent_node; + + gtk_tree_path_up (path); + gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (tree_store), path, &parent_iter); + } + } + + gtk_tree_path_free (path); + + validate_tree (tree_store); +} + +/** + * gtk_tree_store_insert_with_values: + * @tree_store: A `GtkTreeStore` + * @iter: (out) (optional): An unset `GtkTreeIter` to set the new row + * @parent: (nullable): A valid `GtkTreeIter` + * @position: position to insert the new row, or -1 to append after existing rows + * @...: pairs of column number and value, terminated with -1 + * + * Creates a new row at @position. @iter will be changed to point to this + * new row. If @position is -1, or larger than the number of rows on the list, then + * the new row will be appended to the list. The row will be filled with + * the values given to this function. + * + * Calling + * `gtk_tree_store_insert_with_values (tree_store, iter, position, ...)` + * has the same effect as calling + * |[ + * gtk_tree_store_insert (tree_store, iter, position); + * gtk_tree_store_set (tree_store, iter, ...); + * ]| + * with the different that the former will only emit a row_inserted signal, + * while the latter will emit row_inserted, row_changed and if the tree store + * is sorted, rows_reordered. Since emitting the rows_reordered signal + * repeatedly can affect the performance of the program, + * gtk_tree_store_insert_with_values() should generally be preferred when + * inserting rows in a sorted tree store. + * + * Deprecated: 4.10 + */ +void +gtk_tree_store_insert_with_values (GtkTreeStore *tree_store, + GtkTreeIter *iter, + GtkTreeIter *parent, + int position, + ...) +{ + GtkTreeStorePrivate *priv = tree_store->priv; + GtkTreePath *path; + GNode *parent_node; + GNode *new_node; + GtkTreeIter tmp_iter; + va_list var_args; + gboolean changed = FALSE; + gboolean maybe_need_sort = FALSE; + + g_return_if_fail (GTK_IS_TREE_STORE (tree_store)); + + if (!iter) + iter = &tmp_iter; + + if (parent) + g_return_if_fail (VALID_ITER (parent, tree_store)); + + if (parent) + parent_node = parent->user_data; + else + parent_node = priv->root; + + priv->columns_dirty = TRUE; + + new_node = g_node_new (NULL); + + iter->stamp = priv->stamp; + iter->user_data = new_node; + g_node_insert (parent_node, position, new_node); + + va_start (var_args, position); + gtk_tree_store_set_valist_internal (tree_store, iter, + &changed, &maybe_need_sort, + var_args); + va_end (var_args); + + if (maybe_need_sort && GTK_TREE_STORE_IS_SORTED (tree_store)) + gtk_tree_store_sort_iter_changed (tree_store, iter, priv->sort_column_id, FALSE); + + path = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter); + gtk_tree_model_row_inserted (GTK_TREE_MODEL (tree_store), path, iter); + + if (parent_node != priv->root) + { + if (new_node->prev == NULL && new_node->next == NULL) + { + gtk_tree_path_up (path); + gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (tree_store), path, parent); + } + } + + gtk_tree_path_free (path); + + validate_tree ((GtkTreeStore *)tree_store); +} + +/** + * gtk_tree_store_insert_with_valuesv: (rename-to gtk_tree_store_insert_with_values) + * @tree_store: A `GtkTreeStore` + * @iter: (out) (optional): An unset `GtkTreeIter` to set the new row + * @parent: (nullable): A valid `GtkTreeIter` + * @position: position to insert the new row, or -1 for last + * @columns: (array length=n_values): an array of column numbers + * @values: (array length=n_values): an array of GValues + * @n_values: the length of the @columns and @values arrays + * + * A variant of gtk_tree_store_insert_with_values() which takes + * the columns and values as two arrays, instead of varargs. This + * function is mainly intended for language bindings. + * + * Deprecated: 4.10 + */ +void +gtk_tree_store_insert_with_valuesv (GtkTreeStore *tree_store, + GtkTreeIter *iter, + GtkTreeIter *parent, + int position, + int *columns, + GValue *values, + int n_values) +{ + GtkTreeStorePrivate *priv = tree_store->priv; + GtkTreePath *path; + GNode *parent_node; + GNode *new_node; + GtkTreeIter tmp_iter; + gboolean changed = FALSE; + gboolean maybe_need_sort = FALSE; + + g_return_if_fail (GTK_IS_TREE_STORE (tree_store)); + + if (!iter) + iter = &tmp_iter; + + if (parent) + g_return_if_fail (VALID_ITER (parent, tree_store)); + + if (parent) + parent_node = parent->user_data; + else + parent_node = priv->root; + + priv->columns_dirty = TRUE; + + new_node = g_node_new (NULL); + + iter->stamp = priv->stamp; + iter->user_data = new_node; + g_node_insert (parent_node, position, new_node); + + gtk_tree_store_set_vector_internal (tree_store, iter, + &changed, &maybe_need_sort, + columns, values, n_values); + + if (maybe_need_sort && GTK_TREE_STORE_IS_SORTED (tree_store)) + gtk_tree_store_sort_iter_changed (tree_store, iter, priv->sort_column_id, FALSE); + + path = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter); + gtk_tree_model_row_inserted (GTK_TREE_MODEL (tree_store), path, iter); + + if (parent_node != priv->root) + { + if (new_node->prev == NULL && new_node->next == NULL) + { + gtk_tree_path_up (path); + gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (tree_store), path, parent); + } + } + + gtk_tree_path_free (path); + + validate_tree ((GtkTreeStore *)tree_store); +} + +/** + * gtk_tree_store_prepend: + * @tree_store: A `GtkTreeStore` + * @iter: (out): An unset `GtkTreeIter` to set to the prepended row + * @parent: (nullable): A valid `GtkTreeIter` + * + * Prepends a new row to @tree_store. If @parent is non-%NULL, then it will prepend + * the new row before the first child of @parent, otherwise it will prepend a row + * to the top level. @iter will be changed to point to this new row. The row + * will be empty after this function is called. To fill in values, you need to + * call gtk_tree_store_set() or gtk_tree_store_set_value(). + * + * Deprecated: 4.10 + **/ +void +gtk_tree_store_prepend (GtkTreeStore *tree_store, + GtkTreeIter *iter, + GtkTreeIter *parent) +{ + GtkTreeStorePrivate *priv = tree_store->priv; + GNode *parent_node; + + g_return_if_fail (GTK_IS_TREE_STORE (tree_store)); + g_return_if_fail (iter != NULL); + if (parent != NULL) + g_return_if_fail (VALID_ITER (parent, tree_store)); + + priv->columns_dirty = TRUE; + + if (parent == NULL) + parent_node = priv->root; + else + parent_node = parent->user_data; + + if (parent_node->children == NULL) + { + GtkTreePath *path; + + iter->stamp = priv->stamp; + iter->user_data = g_node_new (NULL); + + g_node_prepend (parent_node, G_NODE (iter->user_data)); + + path = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter); + gtk_tree_model_row_inserted (GTK_TREE_MODEL (tree_store), path, iter); + + if (parent_node != priv->root) + { + gtk_tree_path_up (path); + gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (tree_store), path, parent); + } + gtk_tree_path_free (path); + } + else + { + gtk_tree_store_insert_after (tree_store, iter, parent, NULL); + } + + validate_tree (tree_store); +} + +/** + * gtk_tree_store_append: + * @tree_store: A `GtkTreeStore` + * @iter: (out): An unset `GtkTreeIter` to set to the appended row + * @parent: (nullable): A valid `GtkTreeIter` + * + * Appends a new row to @tree_store. If @parent is non-%NULL, then it will append the + * new row after the last child of @parent, otherwise it will append a row to + * the top level. @iter will be changed to point to this new row. The row will + * be empty after this function is called. To fill in values, you need to call + * gtk_tree_store_set() or gtk_tree_store_set_value(). + * + * Deprecated: 4.10 + **/ +void +gtk_tree_store_append (GtkTreeStore *tree_store, + GtkTreeIter *iter, + GtkTreeIter *parent) +{ + GtkTreeStorePrivate *priv = tree_store->priv; + GNode *parent_node; + + g_return_if_fail (GTK_IS_TREE_STORE (tree_store)); + g_return_if_fail (iter != NULL); + if (parent != NULL) + g_return_if_fail (VALID_ITER (parent, tree_store)); + + if (parent == NULL) + parent_node = priv->root; + else + parent_node = parent->user_data; + + priv->columns_dirty = TRUE; + + if (parent_node->children == NULL) + { + GtkTreePath *path; + + iter->stamp = priv->stamp; + iter->user_data = g_node_new (NULL); + + g_node_append (parent_node, G_NODE (iter->user_data)); + + path = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter); + gtk_tree_model_row_inserted (GTK_TREE_MODEL (tree_store), path, iter); + + if (parent_node != priv->root) + { + gtk_tree_path_up (path); + gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (tree_store), path, parent); + } + gtk_tree_path_free (path); + } + else + { + gtk_tree_store_insert_before (tree_store, iter, parent, NULL); + } + + validate_tree (tree_store); +} + +/** + * gtk_tree_store_is_ancestor: + * @tree_store: A `GtkTreeStore` + * @iter: A valid `GtkTreeIter` + * @descendant: A valid `GtkTreeIter` + * + * Returns %TRUE if @iter is an ancestor of @descendant. That is, @iter is the + * parent (or grandparent or great-grandparent) of @descendant. + * + * Returns: %TRUE, if @iter is an ancestor of @descendant + * + * Deprecated: 4.10 + **/ +gboolean +gtk_tree_store_is_ancestor (GtkTreeStore *tree_store, + GtkTreeIter *iter, + GtkTreeIter *descendant) +{ + g_return_val_if_fail (GTK_IS_TREE_STORE (tree_store), FALSE); + g_return_val_if_fail (VALID_ITER (iter, tree_store), FALSE); + g_return_val_if_fail (VALID_ITER (descendant, tree_store), FALSE); + + return g_node_is_ancestor (G_NODE (iter->user_data), + G_NODE (descendant->user_data)); +} + + +/** + * gtk_tree_store_iter_depth: + * @tree_store: A `GtkTreeStore` + * @iter: A valid `GtkTreeIter` + * + * Returns the depth of @iter. This will be 0 for anything on the root level, 1 + * for anything down a level, etc. + * + * Returns: The depth of @iter + * + * Deprecated: 4.10 + **/ +int +gtk_tree_store_iter_depth (GtkTreeStore *tree_store, + GtkTreeIter *iter) +{ + g_return_val_if_fail (GTK_IS_TREE_STORE (tree_store), 0); + g_return_val_if_fail (VALID_ITER (iter, tree_store), 0); + + return g_node_depth (G_NODE (iter->user_data)) - 2; +} + +/* simple ripoff from g_node_traverse_post_order */ +static gboolean +gtk_tree_store_clear_traverse (GNode *node, + GtkTreeStore *store) +{ + GtkTreeIter iter; + + if (node->children) + { + GNode *child; + + child = node->children; + while (child) + { + register GNode *current; + + current = child; + child = current->next; + if (gtk_tree_store_clear_traverse (current, store)) + return TRUE; + } + + if (node->parent) + { + iter.stamp = store->priv->stamp; + iter.user_data = node; + + gtk_tree_store_remove (store, &iter); + } + } + else if (node->parent) + { + iter.stamp = store->priv->stamp; + iter.user_data = node; + + gtk_tree_store_remove (store, &iter); + } + + return FALSE; +} + +static void +gtk_tree_store_increment_stamp (GtkTreeStore *tree_store) +{ + GtkTreeStorePrivate *priv = tree_store->priv; + do + { + priv->stamp++; + } + while (priv->stamp == 0); +} + +/** + * gtk_tree_store_clear: + * @tree_store: a `GtkTreeStore` + * + * Removes all rows from @tree_store + * + * Deprecated: 4.10 + **/ +void +gtk_tree_store_clear (GtkTreeStore *tree_store) +{ + g_return_if_fail (GTK_IS_TREE_STORE (tree_store)); + + gtk_tree_store_clear_traverse (tree_store->priv->root, tree_store); + gtk_tree_store_increment_stamp (tree_store); +} + +static gboolean +gtk_tree_store_iter_is_valid_helper (GtkTreeIter *iter, + GNode *first) +{ + GNode *node; + + node = first; + + do + { + if (node == iter->user_data) + return TRUE; + + if (node->children) + if (gtk_tree_store_iter_is_valid_helper (iter, node->children)) + return TRUE; + + node = node->next; + } + while (node); + + return FALSE; +} + +/** + * gtk_tree_store_iter_is_valid: + * @tree_store: a tree store + * @iter: the iterator to check + * + * Checks if the given iter is a valid iter for this `GtkTreeStore`. + * + * This function is slow. Only use it for debugging and/or testing + * purposes. + * + * Returns: %TRUE if the iter is valid, %FALSE if the iter is invalid. + * + * Deprecated: 4.10 + **/ +gboolean +gtk_tree_store_iter_is_valid (GtkTreeStore *tree_store, + GtkTreeIter *iter) +{ + g_return_val_if_fail (GTK_IS_TREE_STORE (tree_store), FALSE); + g_return_val_if_fail (iter != NULL, FALSE); + + if (!VALID_ITER (iter, tree_store)) + return FALSE; + + return gtk_tree_store_iter_is_valid_helper (iter, tree_store->priv->root); +} + +/* DND */ + + +static gboolean real_gtk_tree_store_row_draggable (GtkTreeDragSource *drag_source, + GtkTreePath *path) +{ + return TRUE; +} + +static gboolean +gtk_tree_store_drag_data_delete (GtkTreeDragSource *drag_source, + GtkTreePath *path) +{ + GtkTreeIter iter; + + if (gtk_tree_store_get_iter (GTK_TREE_MODEL (drag_source), + &iter, + path)) + { + gtk_tree_store_remove (GTK_TREE_STORE (drag_source), + &iter); + return TRUE; + } + else + { + return FALSE; + } +} + +static GdkContentProvider * +gtk_tree_store_drag_data_get (GtkTreeDragSource *drag_source, + GtkTreePath *path) +{ + /* Note that we don't need to handle the GTK_TREE_MODEL_ROW + * target, because the default handler does it for us, but + * we do anyway for the convenience of someone maybe overriding the + * default handler. + */ + return gtk_tree_create_row_drag_content (GTK_TREE_MODEL (drag_source), path); +} + +static void +copy_node_data (GtkTreeStore *tree_store, + GtkTreeIter *src_iter, + GtkTreeIter *dest_iter) +{ + GtkTreeDataList *dl = G_NODE (src_iter->user_data)->data; + GtkTreeDataList *copy_head = NULL; + GtkTreeDataList *copy_prev = NULL; + GtkTreeDataList *copy_iter = NULL; + GtkTreePath *path; + int col; + + col = 0; + while (dl) + { + copy_iter = _gtk_tree_data_list_node_copy (dl, tree_store->priv->column_headers[col]); + + if (copy_head == NULL) + copy_head = copy_iter; + + if (copy_prev) + copy_prev->next = copy_iter; + + copy_prev = copy_iter; + + dl = dl->next; + ++col; + } + + G_NODE (dest_iter->user_data)->data = copy_head; + + path = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), dest_iter); + gtk_tree_model_row_changed (GTK_TREE_MODEL (tree_store), path, dest_iter); + gtk_tree_path_free (path); +} + +static void +recursive_node_copy (GtkTreeStore *tree_store, + GtkTreeIter *src_iter, + GtkTreeIter *dest_iter) +{ + GtkTreeIter child; + GtkTreeModel *model; + + model = GTK_TREE_MODEL (tree_store); + + copy_node_data (tree_store, src_iter, dest_iter); + + if (gtk_tree_store_iter_children (model, + &child, + src_iter)) + { + /* Need to create children and recurse. Note our + * dependence on persistent iterators here. + */ + do + { + GtkTreeIter copy; + + /* Gee, a really slow algorithm... ;-) FIXME */ + gtk_tree_store_append (tree_store, + ©, + dest_iter); + + recursive_node_copy (tree_store, &child, ©); + } + while (gtk_tree_store_iter_next (model, &child)); + } +} + +static gboolean +gtk_tree_store_drag_data_received (GtkTreeDragDest *drag_dest, + GtkTreePath *dest, + const GValue *value) +{ + GtkTreeModel *tree_model; + GtkTreeStore *tree_store; + GtkTreeModel *src_model = NULL; + GtkTreePath *src_path = NULL; + gboolean retval = FALSE; + + tree_model = GTK_TREE_MODEL (drag_dest); + tree_store = GTK_TREE_STORE (drag_dest); + + validate_tree (tree_store); + + if (gtk_tree_get_row_drag_data (value, + &src_model, + &src_path) && + src_model == tree_model) + { + /* Copy the given row to a new position */ + GtkTreeIter src_iter; + GtkTreeIter dest_iter; + GtkTreePath *prev; + + if (!gtk_tree_store_get_iter (src_model, + &src_iter, + src_path)) + { + goto out; + } + + /* Get the path to insert _after_ (dest is the path to insert _before_) */ + prev = gtk_tree_path_copy (dest); + + if (!gtk_tree_path_prev (prev)) + { + GtkTreeIter dest_parent; + GtkTreePath *parent; + GtkTreeIter *dest_parent_p; + + /* dest was the first spot at the current depth; which means + * we are supposed to prepend. + */ + + /* Get the parent, NULL if parent is the root */ + dest_parent_p = NULL; + parent = gtk_tree_path_copy (dest); + if (gtk_tree_path_up (parent) && + gtk_tree_path_get_depth (parent) > 0) + { + gtk_tree_store_get_iter (tree_model, + &dest_parent, + parent); + dest_parent_p = &dest_parent; + } + gtk_tree_path_free (parent); + parent = NULL; + + gtk_tree_store_prepend (tree_store, + &dest_iter, + dest_parent_p); + + retval = TRUE; + } + else + { + if (gtk_tree_store_get_iter (tree_model, &dest_iter, prev)) + { + GtkTreeIter tmp_iter = dest_iter; + + gtk_tree_store_insert_after (tree_store, &dest_iter, NULL, + &tmp_iter); + + retval = TRUE; + } + } + + gtk_tree_path_free (prev); + + /* If we succeeded in creating dest_iter, walk src_iter tree branch, + * duplicating it below dest_iter. + */ + + if (retval) + { + recursive_node_copy (tree_store, + &src_iter, + &dest_iter); + } + } + else + { + /* FIXME maybe add some data targets eventually, or handle text + * targets in the simple case. + */ + + } + + out: + + if (src_path) + gtk_tree_path_free (src_path); + + return retval; +} + +static gboolean +gtk_tree_store_row_drop_possible (GtkTreeDragDest *drag_dest, + GtkTreePath *dest_path, + const GValue *value) +{ + GtkTreeModel *src_model = NULL; + GtkTreePath *src_path = NULL; + GtkTreePath *tmp = NULL; + gboolean retval = FALSE; + + /* don't accept drops if the tree has been sorted */ + if (GTK_TREE_STORE_IS_SORTED (drag_dest)) + return FALSE; + + if (!gtk_tree_get_row_drag_data (value, + &src_model, + &src_path)) + goto out; + + /* can only drag to ourselves */ + if (src_model != GTK_TREE_MODEL (drag_dest)) + goto out; + + /* Can't drop into ourself. */ + if (gtk_tree_path_is_ancestor (src_path, + dest_path)) + goto out; + + /* Can't drop if dest_path's parent doesn't exist */ + { + GtkTreeIter iter; + + if (gtk_tree_path_get_depth (dest_path) > 1) + { + tmp = gtk_tree_path_copy (dest_path); + gtk_tree_path_up (tmp); + + if (!gtk_tree_store_get_iter (GTK_TREE_MODEL (drag_dest), + &iter, tmp)) + goto out; + } + } + + /* Can otherwise drop anywhere. */ + retval = TRUE; + + out: + + if (src_path) + gtk_tree_path_free (src_path); + if (tmp) + gtk_tree_path_free (tmp); + + return retval; +} + +/* Sorting and reordering */ +typedef struct _SortTuple +{ + int offset; + GNode *node; +} SortTuple; + +/* Reordering */ +static int +gtk_tree_store_reorder_func (gconstpointer a, + gconstpointer b, + gpointer user_data) +{ + SortTuple *a_reorder; + SortTuple *b_reorder; + + a_reorder = (SortTuple *)a; + b_reorder = (SortTuple *)b; + + if (a_reorder->offset < b_reorder->offset) + return -1; + if (a_reorder->offset > b_reorder->offset) + return 1; + + return 0; +} + +/** + * gtk_tree_store_reorder: (skip) + * @tree_store: A `GtkTreeStore` + * @parent: (nullable): A `GtkTreeIter` + * @new_order: (array): an array of integers mapping the new position of each child + * to its old position before the re-ordering, + * i.e. @new_order`[newpos] = oldpos`. + * + * Reorders the children of @parent in @tree_store to follow the order + * indicated by @new_order. Note that this function only works with + * unsorted stores. + * + * Deprecated: 4.10 + */ +void +gtk_tree_store_reorder (GtkTreeStore *tree_store, + GtkTreeIter *parent, + int *new_order) +{ + int i, length = 0; + GNode *level, *node; + GtkTreePath *path; + SortTuple *sort_array; + + g_return_if_fail (GTK_IS_TREE_STORE (tree_store)); + g_return_if_fail (!GTK_TREE_STORE_IS_SORTED (tree_store)); + g_return_if_fail (parent == NULL || VALID_ITER (parent, tree_store)); + g_return_if_fail (new_order != NULL); + + if (!parent) + level = G_NODE (tree_store->priv->root)->children; + else + level = G_NODE (parent->user_data)->children; + + if (G_UNLIKELY (!level)) + { + g_warning ("%s: Cannot reorder, parent has no children", G_STRLOC); + return; + } + + /* count nodes */ + node = level; + while (node) + { + length++; + node = node->next; + } + + /* set up sortarray */ + sort_array = g_new (SortTuple, length); + + node = level; + for (i = 0; i < length; i++) + { + sort_array[new_order[i]].offset = i; + sort_array[i].node = node; + + node = node->next; + } + + g_qsort_with_data (sort_array, + length, + sizeof (SortTuple), + gtk_tree_store_reorder_func, + NULL); + + /* fix up level */ + for (i = 0; i < length - 1; i++) + { + sort_array[i].node->next = sort_array[i+1].node; + sort_array[i+1].node->prev = sort_array[i].node; + } + + sort_array[length-1].node->next = NULL; + sort_array[0].node->prev = NULL; + if (parent) + G_NODE (parent->user_data)->children = sort_array[0].node; + else + G_NODE (tree_store->priv->root)->children = sort_array[0].node; + + /* emit signal */ + if (parent) + path = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), parent); + else + path = gtk_tree_path_new (); + gtk_tree_model_rows_reordered (GTK_TREE_MODEL (tree_store), path, + parent, new_order); + gtk_tree_path_free (path); + g_free (sort_array); +} + +/** + * gtk_tree_store_swap: + * @tree_store: A `GtkTreeStore`. + * @a: A `GtkTreeIter`. + * @b: Another `GtkTreeIter`. + * + * Swaps @a and @b in the same level of @tree_store. Note that this function + * only works with unsorted stores. + * + * Deprecated: 4.10 + **/ +void +gtk_tree_store_swap (GtkTreeStore *tree_store, + GtkTreeIter *a, + GtkTreeIter *b) +{ + GNode *tmp, *node_a, *node_b, *parent_node; + GNode *a_prev, *a_next, *b_prev, *b_next; + int i, a_count, b_count, length, *order; + GtkTreePath *path_a, *path_b; + GtkTreeIter parent; + + g_return_if_fail (GTK_IS_TREE_STORE (tree_store)); + g_return_if_fail (VALID_ITER (a, tree_store)); + g_return_if_fail (VALID_ITER (b, tree_store)); + + node_a = G_NODE (a->user_data); + node_b = G_NODE (b->user_data); + + /* basic sanity checking */ + if (node_a == node_b) + return; + + path_a = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), a); + path_b = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), b); + + g_return_if_fail (path_a && path_b); + + gtk_tree_path_up (path_a); + gtk_tree_path_up (path_b); + + if (gtk_tree_path_get_depth (path_a) == 0 + || gtk_tree_path_get_depth (path_b) == 0) + { + if (gtk_tree_path_get_depth (path_a) != gtk_tree_path_get_depth (path_b)) + { + gtk_tree_path_free (path_a); + gtk_tree_path_free (path_b); + + g_warning ("Given children are not in the same level\n"); + return; + } + parent_node = G_NODE (tree_store->priv->root); + } + else + { + if (gtk_tree_path_compare (path_a, path_b)) + { + gtk_tree_path_free (path_a); + gtk_tree_path_free (path_b); + + g_warning ("Given children don't have a common parent\n"); + return; + } + gtk_tree_store_get_iter (GTK_TREE_MODEL (tree_store), &parent, + path_a); + parent_node = G_NODE (parent.user_data); + } + gtk_tree_path_free (path_b); + + /* old links which we have to keep around */ + a_prev = node_a->prev; + a_next = node_a->next; + + b_prev = node_b->prev; + b_next = node_b->next; + + /* fix up links if the nodes are next to each other */ + if (a_prev == node_b) + a_prev = node_a; + if (a_next == node_b) + a_next = node_a; + + if (b_prev == node_a) + b_prev = node_b; + if (b_next == node_a) + b_next = node_b; + + /* counting nodes */ + tmp = parent_node->children; + i = a_count = b_count = 0; + while (tmp) + { + if (tmp == node_a) + a_count = i; + if (tmp == node_b) + b_count = i; + + tmp = tmp->next; + i++; + } + length = i; + + /* hacking the tree */ + if (!a_prev) + parent_node->children = node_b; + else + a_prev->next = node_b; + + if (a_next) + a_next->prev = node_b; + + if (!b_prev) + parent_node->children = node_a; + else + b_prev->next = node_a; + + if (b_next) + b_next->prev = node_a; + + node_a->prev = b_prev; + node_a->next = b_next; + + node_b->prev = a_prev; + node_b->next = a_next; + + /* emit signal */ + order = g_new (int, length); + for (i = 0; i < length; i++) + if (i == a_count) + order[i] = b_count; + else if (i == b_count) + order[i] = a_count; + else + order[i] = i; + + gtk_tree_model_rows_reordered (GTK_TREE_MODEL (tree_store), path_a, + parent_node == tree_store->priv->root + ? NULL : &parent, order); + gtk_tree_path_free (path_a); + g_free (order); +} + +/* WARNING: this function is *incredibly* fragile. Please smashtest after + * making changes here. + * -Kris + */ +static void +gtk_tree_store_move (GtkTreeStore *tree_store, + GtkTreeIter *iter, + GtkTreeIter *position, + gboolean before) +{ + GNode *parent, *node, *a, *b, *tmp, *tmp_a, *tmp_b; + int old_pos, new_pos, length, i, *order; + GtkTreePath *path = NULL, *tmppath, *pos_path = NULL; + GtkTreeIter parent_iter = { 0, }; + GtkTreeIter dst_a = { 0, }; + GtkTreeIter dst_b = { 0, }; + int depth = 0; + gboolean handle_b = TRUE; + + g_return_if_fail (GTK_IS_TREE_STORE (tree_store)); + g_return_if_fail (!GTK_TREE_STORE_IS_SORTED (tree_store)); + g_return_if_fail (VALID_ITER (iter, tree_store)); + if (position) + g_return_if_fail (VALID_ITER (position, tree_store)); + + a = b = NULL; + + /* sanity checks */ + if (position) + { + path = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter); + pos_path = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), + position); + + /* if before: + * moving the iter before path or "path + 1" doesn't make sense + * else + * moving the iter before path or "path - 1" doesn't make sense + */ + if (!gtk_tree_path_compare (path, pos_path)) + goto free_paths_and_out; + + if (before) + gtk_tree_path_next (path); + else + gtk_tree_path_prev (path); + + if (!gtk_tree_path_compare (path, pos_path)) + goto free_paths_and_out; + + if (before) + gtk_tree_path_prev (path); + else + gtk_tree_path_next (path); + + if (gtk_tree_path_get_depth (path) != gtk_tree_path_get_depth (pos_path)) + { + g_warning ("Given children are not in the same level\n"); + + goto free_paths_and_out; + } + + tmppath = gtk_tree_path_copy (pos_path); + gtk_tree_path_up (path); + gtk_tree_path_up (tmppath); + + if (gtk_tree_path_get_depth (path) > 0 && + gtk_tree_path_compare (path, tmppath)) + { + g_warning ("Given children are not in the same level\n"); + + gtk_tree_path_free (tmppath); + goto free_paths_and_out; + } + + gtk_tree_path_free (tmppath); + } + + if (!path) + { + path = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter); + gtk_tree_path_up (path); + } + + depth = gtk_tree_path_get_depth (path); + + if (depth) + { + gtk_tree_store_get_iter (GTK_TREE_MODEL (tree_store), + &parent_iter, path); + + parent = G_NODE (parent_iter.user_data); + } + else + parent = G_NODE (tree_store->priv->root); + + /* yes, I know that this can be done shorter, but I'm doing it this way + * so the code is also maintainable + */ + + if (before && position) + { + b = G_NODE (position->user_data); + + if (gtk_tree_path_get_indices (pos_path)[gtk_tree_path_get_depth (pos_path) - 1] > 0) + { + gtk_tree_path_prev (pos_path); + if (gtk_tree_store_get_iter (GTK_TREE_MODEL (tree_store), + &dst_a, pos_path)) + a = G_NODE (dst_a.user_data); + else + a = NULL; + gtk_tree_path_next (pos_path); + } + + /* if b is NULL, a is NULL too -- we are at the beginning of the list + * yes and we leak memory here ... + */ + g_return_if_fail (b); + } + else if (before && !position) + { + /* move before without position is appending */ + a = NULL; + b = NULL; + } + else /* !before */ + { + if (position) + a = G_NODE (position->user_data); + else + a = NULL; + + if (position) + { + gtk_tree_path_next (pos_path); + if (gtk_tree_store_get_iter (GTK_TREE_MODEL (tree_store), &dst_b, pos_path)) + b = G_NODE (dst_b.user_data); + else + b = NULL; + gtk_tree_path_prev (pos_path); + } + else + { + /* move after without position is prepending */ + if (depth) + gtk_tree_store_iter_children (GTK_TREE_MODEL (tree_store), &dst_b, + &parent_iter); + else + gtk_tree_store_iter_children (GTK_TREE_MODEL (tree_store), &dst_b, + NULL); + + b = G_NODE (dst_b.user_data); + } + + /* if a is NULL, b is NULL too -- we are at the end of the list + * yes and we leak memory here ... + */ + if (position) + g_return_if_fail (a); + } + + /* counting nodes */ + tmp = parent->children; + + length = old_pos = 0; + while (tmp) + { + if (tmp == iter->user_data) + old_pos = length; + + tmp = tmp->next; + length++; + } + + /* remove node from list */ + node = G_NODE (iter->user_data); + tmp_a = node->prev; + tmp_b = node->next; + + if (tmp_a) + tmp_a->next = tmp_b; + else + parent->children = tmp_b; + + if (tmp_b) + tmp_b->prev = tmp_a; + + /* and reinsert the node */ + if (a) + { + tmp = a->next; + + a->next = node; + node->next = tmp; + node->prev = a; + } + else if (!a && !before) + { + tmp = parent->children; + + node->prev = NULL; + parent->children = node; + + node->next = tmp; + if (tmp) + tmp->prev = node; + + handle_b = FALSE; + } + else if (!a && before) + { + if (!position) + { + node->parent = NULL; + node->next = node->prev = NULL; + + /* before with sibling = NULL appends */ + g_node_insert_before (parent, NULL, node); + } + else + { + node->parent = NULL; + node->next = node->prev = NULL; + + /* after with sibling = NULL prepends */ + g_node_insert_after (parent, NULL, node); + } + + handle_b = FALSE; + } + + if (handle_b) + { + if (b) + { + tmp = b->prev; + + b->prev = node; + node->prev = tmp; + node->next = b; + } + else if (!(!a && before)) /* !a && before is completely handled above */ + node->next = NULL; + } + + /* emit signal */ + if (position) + new_pos = gtk_tree_path_get_indices (pos_path)[gtk_tree_path_get_depth (pos_path)-1]; + else if (before) + { + if (depth) + new_pos = gtk_tree_store_iter_n_children (GTK_TREE_MODEL (tree_store), + &parent_iter) - 1; + else + new_pos = gtk_tree_store_iter_n_children (GTK_TREE_MODEL (tree_store), + NULL) - 1; + } + else + new_pos = 0; + + if (new_pos > old_pos) + { + if (before && position) + new_pos--; + } + else + { + if (!before && position) + new_pos++; + } + + order = g_new (int, length); + if (new_pos > old_pos) + { + for (i = 0; i < length; i++) + if (i < old_pos) + order[i] = i; + else if (i >= old_pos && i < new_pos) + order[i] = i + 1; + else if (i == new_pos) + order[i] = old_pos; + else + order[i] = i; + } + else + { + for (i = 0; i < length; i++) + if (i == new_pos) + order[i] = old_pos; + else if (i > new_pos && i <= old_pos) + order[i] = i - 1; + else + order[i] = i; + } + + if (depth) + { + tmppath = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), + &parent_iter); + gtk_tree_model_rows_reordered (GTK_TREE_MODEL (tree_store), + tmppath, &parent_iter, order); + } + else + { + tmppath = gtk_tree_path_new (); + gtk_tree_model_rows_reordered (GTK_TREE_MODEL (tree_store), + tmppath, NULL, order); + } + + gtk_tree_path_free (tmppath); + gtk_tree_path_free (path); + if (position) + gtk_tree_path_free (pos_path); + g_free (order); + + return; + +free_paths_and_out: + gtk_tree_path_free (path); + gtk_tree_path_free (pos_path); +} + +/** + * gtk_tree_store_move_before: + * @tree_store: A `GtkTreeStore` + * @iter: A `GtkTreeIter` + * @position: (nullable): A `GtkTreeIter` + * + * Moves @iter in @tree_store to the position before @position. @iter and + * @position should be in the same level. Note that this function only + * works with unsorted stores. If @position is %NULL, @iter will be + * moved to the end of the level. + * + * Deprecated: 4.10 + **/ +void +gtk_tree_store_move_before (GtkTreeStore *tree_store, + GtkTreeIter *iter, + GtkTreeIter *position) +{ + gtk_tree_store_move (tree_store, iter, position, TRUE); +} + +/** + * gtk_tree_store_move_after: + * @tree_store: A `GtkTreeStore` + * @iter: A `GtkTreeIter`. + * @position: (nullable): A `GtkTreeIter`. + * + * Moves @iter in @tree_store to the position after @position. @iter and + * @position should be in the same level. Note that this function only + * works with unsorted stores. If @position is %NULL, @iter will be moved + * to the start of the level. + * + * Deprecated: 4.10 + **/ +void +gtk_tree_store_move_after (GtkTreeStore *tree_store, + GtkTreeIter *iter, + GtkTreeIter *position) +{ + gtk_tree_store_move (tree_store, iter, position, FALSE); +} + +/* Sorting */ +static int +gtk_tree_store_compare_func (gconstpointer a, + gconstpointer b, + gpointer user_data) +{ + GtkTreeStore *tree_store = user_data; + GtkTreeStorePrivate *priv = tree_store->priv; + GNode *node_a; + GNode *node_b; + GtkTreeIterCompareFunc func; + gpointer data; + + GtkTreeIter iter_a; + GtkTreeIter iter_b; + int retval; + + if (priv->sort_column_id != -1) + { + GtkTreeDataSortHeader *header; + + header = _gtk_tree_data_list_get_header (priv->sort_list, + priv->sort_column_id); + g_return_val_if_fail (header != NULL, 0); + g_return_val_if_fail (header->func != NULL, 0); + + func = header->func; + data = header->data; + } + else + { + g_return_val_if_fail (priv->default_sort_func != NULL, 0); + func = priv->default_sort_func; + data = priv->default_sort_data; + } + + node_a = ((SortTuple *) a)->node; + node_b = ((SortTuple *) b)->node; + + iter_a.stamp = priv->stamp; + iter_a.user_data = node_a; + iter_b.stamp = priv->stamp; + iter_b.user_data = node_b; + + retval = (* func) (GTK_TREE_MODEL (user_data), &iter_a, &iter_b, data); + + if (priv->order == GTK_SORT_DESCENDING) + { + if (retval > 0) + retval = -1; + else if (retval < 0) + retval = 1; + } + return retval; +} + +static void +gtk_tree_store_sort_helper (GtkTreeStore *tree_store, + GNode *parent, + gboolean recurse) +{ + GtkTreeIter iter; + GArray *sort_array; + GNode *node; + GNode *tmp_node; + int list_length; + int i; + int *new_order; + GtkTreePath *path; + + node = parent->children; + if (node == NULL || node->next == NULL) + { + if (recurse && node && node->children) + gtk_tree_store_sort_helper (tree_store, node, TRUE); + + return; + } + + list_length = 0; + for (tmp_node = node; tmp_node; tmp_node = tmp_node->next) + list_length++; + + sort_array = g_array_sized_new (FALSE, FALSE, sizeof (SortTuple), list_length); + + i = 0; + for (tmp_node = node; tmp_node; tmp_node = tmp_node->next) + { + SortTuple tuple; + + tuple.offset = i; + tuple.node = tmp_node; + g_array_append_val (sort_array, tuple); + i++; + } + + /* Sort the array */ + g_array_sort_with_data (sort_array, gtk_tree_store_compare_func, tree_store); + + for (i = 0; i < list_length - 1; i++) + { + g_array_index (sort_array, SortTuple, i).node->next = + g_array_index (sort_array, SortTuple, i + 1).node; + g_array_index (sort_array, SortTuple, i + 1).node->prev = + g_array_index (sort_array, SortTuple, i).node; + } + g_array_index (sort_array, SortTuple, list_length - 1).node->next = NULL; + g_array_index (sort_array, SortTuple, 0).node->prev = NULL; + parent->children = g_array_index (sort_array, SortTuple, 0).node; + + /* Let the world know about our new order */ + new_order = g_new (int, list_length); + for (i = 0; i < list_length; i++) + new_order[i] = g_array_index (sort_array, SortTuple, i).offset; + + iter.stamp = tree_store->priv->stamp; + iter.user_data = parent; + path = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), &iter); + gtk_tree_model_rows_reordered (GTK_TREE_MODEL (tree_store), + path, &iter, new_order); + gtk_tree_path_free (path); + g_free (new_order); + g_array_free (sort_array, TRUE); + + if (recurse) + { + for (tmp_node = parent->children; tmp_node; tmp_node = tmp_node->next) + { + if (tmp_node->children) + gtk_tree_store_sort_helper (tree_store, tmp_node, TRUE); + } + } +} + +static void +gtk_tree_store_sort (GtkTreeStore *tree_store) +{ + GtkTreeStorePrivate *priv = tree_store->priv; + + if (!GTK_TREE_STORE_IS_SORTED (tree_store)) + return; + + if (priv->sort_column_id != -1) + { + GtkTreeDataSortHeader *header = NULL; + + header = _gtk_tree_data_list_get_header (priv->sort_list, + priv->sort_column_id); + + /* We want to make sure that we have a function */ + g_return_if_fail (header != NULL); + g_return_if_fail (header->func != NULL); + } + else + { + g_return_if_fail (priv->default_sort_func != NULL); + } + + gtk_tree_store_sort_helper (tree_store, G_NODE (priv->root), TRUE); +} + +static void +gtk_tree_store_sort_iter_changed (GtkTreeStore *tree_store, + GtkTreeIter *iter, + int column, + gboolean emit_signal) +{ + GtkTreeStorePrivate *priv = tree_store->priv; + GNode *prev = NULL; + GNode *next = NULL; + GNode *node; + GtkTreePath *tmp_path; + GtkTreeIter tmp_iter; + int cmp_a = 0; + int cmp_b = 0; + int i; + int old_location; + int new_location; + int *new_order; + int length; + GtkTreeIterCompareFunc func; + gpointer data; + + g_return_if_fail (G_NODE (iter->user_data)->parent != NULL); + + tmp_iter.stamp = priv->stamp; + if (priv->sort_column_id != -1) + { + GtkTreeDataSortHeader *header; + header = _gtk_tree_data_list_get_header (priv->sort_list, + priv->sort_column_id); + g_return_if_fail (header != NULL); + g_return_if_fail (header->func != NULL); + func = header->func; + data = header->data; + } + else + { + g_return_if_fail (priv->default_sort_func != NULL); + func = priv->default_sort_func; + data = priv->default_sort_data; + } + + /* If it's the built in function, we don't sort. */ + if (func == _gtk_tree_data_list_compare_func && + priv->sort_column_id != column) + return; + + old_location = 0; + node = G_NODE (iter->user_data)->parent->children; + /* First we find the iter, its prev, and its next */ + while (node) + { + if (node == G_NODE (iter->user_data)) + break; + old_location++; + node = node->next; + } + g_assert (node != NULL); + + prev = node->prev; + next = node->next; + + /* Check the common case, where we don't need to sort it moved. */ + if (prev != NULL) + { + tmp_iter.user_data = prev; + cmp_a = (* func) (GTK_TREE_MODEL (tree_store), &tmp_iter, iter, data); + } + + if (next != NULL) + { + tmp_iter.user_data = next; + cmp_b = (* func) (GTK_TREE_MODEL (tree_store), iter, &tmp_iter, data); + } + + if (priv->order == GTK_SORT_DESCENDING) + { + if (cmp_a < 0) + cmp_a = 1; + else if (cmp_a > 0) + cmp_a = -1; + + if (cmp_b < 0) + cmp_b = 1; + else if (cmp_b > 0) + cmp_b = -1; + } + + if (prev == NULL && cmp_b <= 0) + return; + else if (next == NULL && cmp_a <= 0) + return; + else if (prev != NULL && next != NULL && + cmp_a <= 0 && cmp_b <= 0) + return; + + /* We actually need to sort it */ + /* First, remove the old link. */ + + if (prev) + prev->next = next; + else + node->parent->children = next; + + if (next) + next->prev = prev; + + node->prev = NULL; + node->next = NULL; + + /* FIXME: as an optimization, we can potentially start at next */ + prev = NULL; + node = node->parent->children; + new_location = 0; + tmp_iter.user_data = node; + if (priv->order == GTK_SORT_DESCENDING) + cmp_a = (* func) (GTK_TREE_MODEL (tree_store), &tmp_iter, iter, data); + else + cmp_a = (* func) (GTK_TREE_MODEL (tree_store), iter, &tmp_iter, data); + + while ((node->next) && (cmp_a > 0)) + { + prev = node; + node = node->next; + new_location++; + tmp_iter.user_data = node; + if (priv->order == GTK_SORT_DESCENDING) + cmp_a = (* func) (GTK_TREE_MODEL (tree_store), &tmp_iter, iter, data); + else + cmp_a = (* func) (GTK_TREE_MODEL (tree_store), iter, &tmp_iter, data); + } + + if ((!node->next) && (cmp_a > 0)) + { + new_location++; + node->next = G_NODE (iter->user_data); + node->next->prev = node; + } + else if (prev) + { + prev->next = G_NODE (iter->user_data); + prev->next->prev = prev; + G_NODE (iter->user_data)->next = node; + G_NODE (iter->user_data)->next->prev = G_NODE (iter->user_data); + } + else + { + G_NODE (iter->user_data)->next = G_NODE (iter->user_data)->parent->children; + G_NODE (iter->user_data)->next->prev = G_NODE (iter->user_data); + G_NODE (iter->user_data)->parent->children = G_NODE (iter->user_data); + } + + if (!emit_signal) + return; + + /* Emit the reordered signal. */ + length = g_node_n_children (node->parent); + new_order = g_new (int, length); + if (old_location < new_location) + for (i = 0; i < length; i++) + { + if (i < old_location || + i > new_location) + new_order[i] = i; + else if (i >= old_location && + i < new_location) + new_order[i] = i + 1; + else if (i == new_location) + new_order[i] = old_location; + } + else + for (i = 0; i < length; i++) + { + if (i < new_location || + i > old_location) + new_order[i] = i; + else if (i > new_location && + i <= old_location) + new_order[i] = i - 1; + else if (i == new_location) + new_order[i] = old_location; + } + + tmp_iter.user_data = node->parent; + tmp_path = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), &tmp_iter); + + gtk_tree_model_rows_reordered (GTK_TREE_MODEL (tree_store), + tmp_path, &tmp_iter, + new_order); + + gtk_tree_path_free (tmp_path); + g_free (new_order); +} + + +static gboolean +gtk_tree_store_get_sort_column_id (GtkTreeSortable *sortable, + int *sort_column_id, + GtkSortType *order) +{ + GtkTreeStore *tree_store = (GtkTreeStore *) sortable; + GtkTreeStorePrivate *priv = tree_store->priv; + + if (sort_column_id) + * sort_column_id = priv->sort_column_id; + if (order) + * order = priv->order; + + if (priv->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID || + priv->sort_column_id == GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID) + return FALSE; + + return TRUE; +} + +static void +gtk_tree_store_set_sort_column_id (GtkTreeSortable *sortable, + int sort_column_id, + GtkSortType order) +{ + GtkTreeStore *tree_store = (GtkTreeStore *) sortable; + GtkTreeStorePrivate *priv = tree_store->priv; + + if ((priv->sort_column_id == sort_column_id) && + (priv->order == order)) + return; + + if (sort_column_id != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID) + { + if (sort_column_id != GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID) + { + GtkTreeDataSortHeader *header = NULL; + + header = _gtk_tree_data_list_get_header (priv->sort_list, + sort_column_id); + + /* We want to make sure that we have a function */ + g_return_if_fail (header != NULL); + g_return_if_fail (header->func != NULL); + } + else + { + g_return_if_fail (priv->default_sort_func != NULL); + } + } + + priv->sort_column_id = sort_column_id; + priv->order = order; + + gtk_tree_sortable_sort_column_changed (sortable); + + gtk_tree_store_sort (tree_store); +} + +static void +gtk_tree_store_set_sort_func (GtkTreeSortable *sortable, + int sort_column_id, + GtkTreeIterCompareFunc func, + gpointer data, + GDestroyNotify destroy) +{ + GtkTreeStore *tree_store = (GtkTreeStore *) sortable; + GtkTreeStorePrivate *priv = tree_store->priv; + + priv->sort_list = _gtk_tree_data_list_set_header (priv->sort_list, + sort_column_id, + func, data, destroy); + + if (priv->sort_column_id == sort_column_id) + gtk_tree_store_sort (tree_store); +} + +static void +gtk_tree_store_set_default_sort_func (GtkTreeSortable *sortable, + GtkTreeIterCompareFunc func, + gpointer data, + GDestroyNotify destroy) +{ + GtkTreeStore *tree_store = (GtkTreeStore *) sortable; + GtkTreeStorePrivate *priv = tree_store->priv; + + if (priv->default_sort_destroy) + { + GDestroyNotify d = priv->default_sort_destroy; + + priv->default_sort_destroy = NULL; + d (priv->default_sort_data); + } + + priv->default_sort_func = func; + priv->default_sort_data = data; + priv->default_sort_destroy = destroy; + + if (priv->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID) + gtk_tree_store_sort (tree_store); +} + +static gboolean +gtk_tree_store_has_default_sort_func (GtkTreeSortable *sortable) +{ + GtkTreeStore *tree_store = (GtkTreeStore *) sortable; + + return (tree_store->priv->default_sort_func != NULL); +} + +#ifdef G_ENABLE_DEBUG +static void +validate_gnode (GNode* node) +{ + GNode *iter; + + iter = node->children; + while (iter != NULL) + { + g_assert (iter->parent == node); + if (iter->prev) + g_assert (iter->prev->next == iter); + validate_gnode (iter); + iter = iter->next; + } +} +#endif + +/* GtkBuildable custom tag implementation + * + * + * + * + * + */ +typedef struct { + gboolean translatable; + char *context; + int id; +} ColInfo; + +typedef struct { + GtkBuilder *builder; + GObject *object; + GSList *column_type_names; + GType *column_types; + GValue *values; + int *colids; + ColInfo **columns; + int last_row; + int n_columns; + int row_column; + gboolean is_data; + const char *domain; + GList *parents; + gboolean row_is_open; +} SubParserData; + +static void +append_current_row (SubParserData *data) +{ + GtkTreeIter *parent; + GtkTreeIter iter; + int i; + + if (data->parents) + parent = data->parents->data; + else + parent = NULL; + + gtk_tree_store_insert_with_valuesv (GTK_TREE_STORE (data->object), + &iter, + parent, + -1, + data->colids, + data->values, + data->row_column); + + data->parents = g_list_prepend (data->parents, gtk_tree_iter_copy (&iter)); + + for (i = 0; i < data->row_column; i++) + { + ColInfo *info = data->columns[i]; + g_free (info->context); + g_slice_free (ColInfo, info); + data->columns[i] = NULL; + g_value_unset (&data->values[i]); + } + g_free (data->values); + data->values = g_new0 (GValue, data->n_columns); + data->last_row++; + data->row_column = 0; + data->row_is_open = FALSE; +} + +static void +tree_store_start_element (GtkBuildableParseContext *context, + const char *element_name, + const char **names, + const char **values, + gpointer user_data, + GError **error) +{ + SubParserData *data = (SubParserData*)user_data; + + if (strcmp (element_name, "col") == 0) + { + int id = -1; + const char *id_str; + const char *msg_context = NULL; + gboolean translatable = FALSE; + ColInfo *info; + GValue val = G_VALUE_INIT; + + if (!_gtk_builder_check_parent (data->builder, context, "row", error)) + return; + + if (!data->row_is_open) + { + g_set_error (error, + GTK_BUILDER_ERROR, + GTK_BUILDER_ERROR_INVALID_TAG, + "Can't use here"); + _gtk_builder_prefix_error (data->builder, context, error); + return; + } + + if (data->row_column >= data->n_columns) + { + g_set_error (error, + GTK_BUILDER_ERROR, GTK_BUILDER_ERROR_INVALID_VALUE, + "Too many columns, maximum is %d", data->n_columns - 1); + _gtk_builder_prefix_error (data->builder, context, error); + return; + } + + if (!g_markup_collect_attributes (element_name, names, values, error, + G_MARKUP_COLLECT_STRING, "id", &id_str, + G_MARKUP_COLLECT_BOOLEAN|G_MARKUP_COLLECT_OPTIONAL, "translatable", &translatable, + G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "comments", NULL, + G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "context", &msg_context, + G_MARKUP_COLLECT_INVALID)) + { + _gtk_builder_prefix_error (data->builder, context, error); + return; + } + + if (!gtk_builder_value_from_string_type (data->builder, G_TYPE_INT, id_str, &val, error)) + { + _gtk_builder_prefix_error (data->builder, context, error); + return; + } + + id = g_value_get_int (&val); + if (id < 0 || id >= data->n_columns) + { + g_set_error (error, + GTK_BUILDER_ERROR, GTK_BUILDER_ERROR_INVALID_VALUE, + "id value %d out of range", id); + _gtk_builder_prefix_error (data->builder, context, error); + return; + } + + info = g_slice_new0 (ColInfo); + info->translatable = translatable; + info->context = g_strdup (msg_context); + info->id = id; + + data->colids[data->row_column] = id; + data->columns[data->row_column] = info; + data->row_column++; + data->is_data = TRUE; + } + else if (strcmp (element_name, "row") == 0) + { + if (!_gtk_builder_check_parents (data->builder, context, error, "data", "row", NULL)) + return; + + if (data->row_is_open) + append_current_row (data); + + if (!g_markup_collect_attributes (element_name, names, values, error, + G_MARKUP_COLLECT_INVALID, NULL, NULL, + G_MARKUP_COLLECT_INVALID)) + _gtk_builder_prefix_error (data->builder, context, error); + + data->row_is_open = TRUE; + } + else if (strcmp (element_name, "columns") == 0 || + strcmp (element_name, "data") == 0) + { + if (!_gtk_builder_check_parent (data->builder, context, "object", error)) + return; + + if (!g_markup_collect_attributes (element_name, names, values, error, + G_MARKUP_COLLECT_INVALID, NULL, NULL, + G_MARKUP_COLLECT_INVALID)) + _gtk_builder_prefix_error (data->builder, context, error); + } + else if (strcmp (element_name, "column") == 0) + { + const char *type; + + if (!_gtk_builder_check_parent (data->builder, context, "columns", error)) + return; + + if (!g_markup_collect_attributes (element_name, names, values, error, + G_MARKUP_COLLECT_STRING, "type", &type, + G_MARKUP_COLLECT_INVALID)) + { + _gtk_builder_prefix_error (data->builder, context, error); + return; + } + + data->column_type_names = g_slist_prepend (data->column_type_names, g_strdup (type)); + } + + else + { + _gtk_builder_error_unhandled_tag (data->builder, context, + "GtkTreeStore", element_name, + error); + } +} + +static void +tree_store_end_element (GtkBuildableParseContext *context, + const char *element_name, + gpointer user_data, + GError **error) +{ + SubParserData *data = (SubParserData*)user_data; + + g_assert(data->builder); + + if (strcmp (element_name, "row") == 0) + { + GtkTreeIter *parent; + + if (data->row_column > 0) + append_current_row (data); + + parent = data->parents->data; + data->parents = g_list_remove (data->parents, parent); + gtk_tree_iter_free (parent); + } + else if (strcmp (element_name, "columns") == 0) + { + GType *column_types; + GSList *l; + int i; + GType type; + + data = (SubParserData*)user_data; + data->column_type_names = g_slist_reverse (data->column_type_names); + column_types = g_new0 (GType, g_slist_length (data->column_type_names)); + + for (l = data->column_type_names, i = 0; l; l = l->next, i++) + { + type = gtk_builder_get_type_from_name (data->builder, l->data); + if (type == G_TYPE_INVALID) + { + g_warning ("Unknown type %s specified in treemodel %s", + (const char *)l->data, + gtk_buildable_get_buildable_id (GTK_BUILDABLE (data->object))); + continue; + } + column_types[i] = type; + + g_free (l->data); + } + + gtk_tree_store_set_column_types (GTK_TREE_STORE (data->object), i, column_types); + + g_free (column_types); + } + else if (strcmp (element_name, "col") == 0) + { + data->is_data = FALSE; + } +} + +static void +tree_store_text (GtkBuildableParseContext *context, + const char *text, + gsize text_len, + gpointer user_data, + GError **error) +{ + SubParserData *data = (SubParserData*)user_data; + int i; + char *string; + ColInfo *info; + + if (!data->is_data) + return; + + i = data->row_column - 1; + info = data->columns[i]; + + string = g_strndup (text, text_len); + if (info->translatable && text_len) + { + char *translated; + + /* FIXME: This will not use the domain set in the .ui file, + * since the parser is not telling the builder about the domain. + * However, it will work for gtk_builder_set_translation_domain() calls. + */ + translated = g_strdup (_gtk_builder_parser_translate (data->domain, + info->context, + string)); + g_free (string); + string = translated; + } + + if (!gtk_builder_value_from_string_type (data->builder, + data->column_types[info->id], + string, + &data->values[i], + error)) + { + _gtk_builder_prefix_error (data->builder, context, error); + } + g_free (string); +} + +static const GtkBuildableParser tree_store_parser = +{ + tree_store_start_element, + tree_store_end_element, + tree_store_text +}; + +static gboolean +gtk_tree_store_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const char *tagname, + GtkBuildableParser *parser, + gpointer *parser_data) +{ + SubParserData *data; + + if (child) + return FALSE; + + if (strcmp (tagname, "columns") == 0) + { + data = g_slice_new0 (SubParserData); + data->builder = builder; + data->column_type_names = NULL; + data->object = G_OBJECT (buildable); + + *parser = tree_store_parser; + *parser_data = data; + + return TRUE; + } + else if (strcmp (tagname, "data") == 0) + { + int n_columns = gtk_tree_store_get_n_columns (GTK_TREE_MODEL (buildable)); + if (n_columns == 0) + g_error ("Cannot append data to an empty model"); + + data = g_slice_new0 (SubParserData); + data->builder = builder; + data->object = G_OBJECT (buildable); + data->values = g_new0 (GValue, n_columns); + data->colids = g_new0 (int, n_columns); + data->columns = g_new0 (ColInfo *, n_columns); + data->column_types = GTK_TREE_STORE (buildable)->priv->column_headers; + data->n_columns = n_columns; + data->last_row = 0; + data->domain = gtk_builder_get_translation_domain (builder); + + *parser = tree_store_parser; + *parser_data = data; + + return TRUE; + } + + return FALSE; +} + +static void +gtk_tree_store_buildable_custom_tag_end (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const char *tagname, + gpointer parser_data) +{ + SubParserData *data = (SubParserData*)parser_data; + + if (strcmp (tagname, "columns") == 0) + { + g_slist_free (data->column_type_names); + g_slice_free (SubParserData, data); + } + else if (strcmp (tagname, "data") == 0) + { + int i; + for (i = 0; i < data->n_columns; i++) + { + ColInfo *info = data->columns[i]; + if (info) + { + g_free (info->context); + g_slice_free (ColInfo, info); + } + } + g_free (data->colids); + g_free (data->columns); + g_free (data->values); + g_slice_free (SubParserData, data); + } +} diff --git a/gtk/deprecated/gtktreestore.h b/gtk/deprecated/gtktreestore.h new file mode 100644 index 0000000000..5b95363030 --- /dev/null +++ b/gtk/deprecated/gtktreestore.h @@ -0,0 +1,169 @@ +/* gtktreestore.h + * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#ifndef __GTK_TREE_STORE_H__ +#define __GTK_TREE_STORE_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include +#include +#include + + +G_BEGIN_DECLS + + +#define GTK_TYPE_TREE_STORE (gtk_tree_store_get_type ()) +#define GTK_TREE_STORE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_TREE_STORE, GtkTreeStore)) +#define GTK_TREE_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_TREE_STORE, GtkTreeStoreClass)) +#define GTK_IS_TREE_STORE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_TREE_STORE)) +#define GTK_IS_TREE_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_TREE_STORE)) +#define GTK_TREE_STORE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_TREE_STORE, GtkTreeStoreClass)) + +typedef struct _GtkTreeStore GtkTreeStore; +typedef struct _GtkTreeStoreClass GtkTreeStoreClass; +typedef struct _GtkTreeStorePrivate GtkTreeStorePrivate; + +struct _GtkTreeStore +{ + GObject parent; + + GtkTreeStorePrivate *priv; +}; + +struct _GtkTreeStoreClass +{ + GObjectClass parent_class; + + /*< private >*/ + gpointer padding[8]; +}; + + +GDK_AVAILABLE_IN_ALL +GType gtk_tree_store_get_type (void) G_GNUC_CONST; +GDK_DEPRECATED_IN_4_10 +GtkTreeStore *gtk_tree_store_new (int n_columns, + ...); +GDK_DEPRECATED_IN_4_10 +GtkTreeStore *gtk_tree_store_newv (int n_columns, + GType *types); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_store_set_column_types (GtkTreeStore *tree_store, + int n_columns, + GType *types); + +/* NOTE: use gtk_tree_model_get to get values from a GtkTreeStore */ + +GDK_DEPRECATED_IN_4_10 +void gtk_tree_store_set_value (GtkTreeStore *tree_store, + GtkTreeIter *iter, + int column, + GValue *value); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_store_set (GtkTreeStore *tree_store, + GtkTreeIter *iter, + ...); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_store_set_valuesv (GtkTreeStore *tree_store, + GtkTreeIter *iter, + int *columns, + GValue *values, + int n_values); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_store_set_valist (GtkTreeStore *tree_store, + GtkTreeIter *iter, + va_list var_args); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_store_remove (GtkTreeStore *tree_store, + GtkTreeIter *iter); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_store_insert (GtkTreeStore *tree_store, + GtkTreeIter *iter, + GtkTreeIter *parent, + int position); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_store_insert_before (GtkTreeStore *tree_store, + GtkTreeIter *iter, + GtkTreeIter *parent, + GtkTreeIter *sibling); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_store_insert_after (GtkTreeStore *tree_store, + GtkTreeIter *iter, + GtkTreeIter *parent, + GtkTreeIter *sibling); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_store_insert_with_values (GtkTreeStore *tree_store, + GtkTreeIter *iter, + GtkTreeIter *parent, + int position, + ...); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_store_insert_with_valuesv (GtkTreeStore *tree_store, + GtkTreeIter *iter, + GtkTreeIter *parent, + int position, + int *columns, + GValue *values, + int n_values); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_store_prepend (GtkTreeStore *tree_store, + GtkTreeIter *iter, + GtkTreeIter *parent); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_store_append (GtkTreeStore *tree_store, + GtkTreeIter *iter, + GtkTreeIter *parent); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_store_is_ancestor (GtkTreeStore *tree_store, + GtkTreeIter *iter, + GtkTreeIter *descendant); +GDK_DEPRECATED_IN_4_10 +int gtk_tree_store_iter_depth (GtkTreeStore *tree_store, + GtkTreeIter *iter); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_store_clear (GtkTreeStore *tree_store); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_store_iter_is_valid (GtkTreeStore *tree_store, + GtkTreeIter *iter); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_store_reorder (GtkTreeStore *tree_store, + GtkTreeIter *parent, + int *new_order); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_store_swap (GtkTreeStore *tree_store, + GtkTreeIter *a, + GtkTreeIter *b); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_store_move_before (GtkTreeStore *tree_store, + GtkTreeIter *iter, + GtkTreeIter *position); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_store_move_after (GtkTreeStore *tree_store, + GtkTreeIter *iter, + GtkTreeIter *position); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkTreeStore, g_object_unref) + +G_END_DECLS + + +#endif /* __GTK_TREE_STORE_H__ */ diff --git a/gtk/deprecated/gtktreeview.c b/gtk/deprecated/gtktreeview.c new file mode 100644 index 0000000000..5403754579 --- /dev/null +++ b/gtk/deprecated/gtktreeview.c @@ -0,0 +1,15045 @@ +/* gtktreeview.c + * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + + +#include "config.h" + +#include "gtktreeview.h" + +#include "gtkadjustmentprivate.h" +#include "gtkbox.h" +#include "gtkbuildable.h" +#include "gtkbutton.h" +#include "gtkcelllayout.h" +#include "gtkcellrenderer.h" +#include "gtkcssnumbervalueprivate.h" +#include "gtkcsscolorvalueprivate.h" +#include "gtkdragsourceprivate.h" +#include "gtkdragicon.h" +#include "gtkdroptargetasync.h" +#include "gtkentryprivate.h" +#include "gtksearchentryprivate.h" +#include "gtkeventcontrollerkey.h" +#include "gtkeventcontrollerfocus.h" +#include "gtkeventcontrollermotion.h" +#include "gtkeventcontrollerscroll.h" +#include "gtkframe.h" +#include "gtkgesturedrag.h" +#include "gtkgestureclick.h" +#include "gtkgesturesingle.h" +#include "gtklabel.h" +#include "gtkmain.h" +#include "gtkmarshalers.h" +#include "gtkprivate.h" +#include "gtktext.h" +#include "gtktreerbtreeprivate.h" +#include "gtkrendericonprivate.h" +#include "gtkscrollable.h" +#include "gtksettingsprivate.h" +#include "gtkshortcutcontroller.h" +#include "gtksnapshot.h" +#include "gtkstylecontextprivate.h" +#include "gtktooltip.h" +#include "gtktreednd.h" +#include "gtktreemodelsort.h" +#include "gtktreeprivate.h" +#include "gtktypebuiltins.h" +#include "gtkwidgetprivate.h" +#include "gtkwindowgroup.h" +#include "gtknative.h" +#include "gtkpopover.h" + +#include "gdk/gdkeventsprivate.h" +#include "gdk/gdktextureprivate.h" + +#include +#include + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + +/** + * GtkTreeView: + * + * A widget for displaying both trees and lists + * + * Widget that displays any object that implements the [iface@Gtk.TreeModel] interface. + * + * Please refer to the [tree widget conceptual overview](section-tree-widget.html) + * for an overview of all the objects and data types related to the tree + * widget and how they work together. + * + * ## Coordinate systems in GtkTreeView API + * + * Several different coordinate systems are exposed in the `GtkTreeView` API. + * These are: + * + * ![](tree-view-coordinates.png) + * + * - Widget coordinates: Coordinates relative to the widget (usually `widget->window`). + * + * - Bin window coordinates: Coordinates relative to the window that GtkTreeView renders to. + * + * - Tree coordinates: Coordinates relative to the entire scrollable area of GtkTreeView. These + * coordinates start at (0, 0) for row 0 of the tree. + * + * Several functions are available for converting between the different + * coordinate systems. The most common translations are between widget and bin + * window coordinates and between bin window and tree coordinates. For the + * former you can use [method@Gtk.TreeView.convert_widget_to_bin_window_coords] + * (and vice versa), for the latter [method@Gtk.TreeView.convert_bin_window_to_tree_coords] + * (and vice versa). + * + * ## `GtkTreeView` as `GtkBuildable` + * + * The `GtkTreeView` implementation of the `GtkBuildable` interface accepts + * [class@Gtk.TreeViewColumn] objects as `` elements and exposes the + * internal [class@Gtk.TreeSelection] in UI definitions. + * + * An example of a UI definition fragment with `GtkTreeView`: + * + * ```xml + * + * liststore1 + * + * + * Test + * + * + * + * 1 + * + * + * + * + * + * + * + * + * + * + * ``` + * + * ## CSS nodes + * + * ``` + * treeview.view + * ├── header + * │ ├── button + * │ │ ╰── [sort-indicator] + * ┊ ┊ + * │ ╰── button + * │ ╰── [sort-indicator] + * │ + * ├── [rubberband] + * ╰── [dndtarget] + * ``` + * + * `GtkTreeView` has a main CSS node with name `treeview` and style class `.view`. + * It has a subnode with name `header`, which is the parent for all the column + * header widgets' CSS nodes. + * + * Each column header consists of a `button`, which among other content, has a + * child with name `sort-indicator`, which carries the `.ascending` or `.descending` + * style classes when the column header should show a sort indicator. The CSS + * is expected to provide a suitable image using the `-gtk-icon-source` property. + * + * For rubberband selection, a subnode with name `rubberband` is used. + * + * For the drop target location during DND, a subnode with name `dndtarget` is used. + */ + +enum +{ + DRAG_COLUMN_WINDOW_STATE_UNSET = 0, + DRAG_COLUMN_WINDOW_STATE_ORIGINAL = 1, + DRAG_COLUMN_WINDOW_STATE_ARROW = 2, + DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT = 3, + DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT = 4 +}; + +enum +{ + RUBBER_BAND_OFF = 0, + RUBBER_BAND_MAYBE_START = 1, + RUBBER_BAND_ACTIVE = 2 +}; + +typedef enum { + CLEAR_AND_SELECT = (1 << 0), + CLAMP_NODE = (1 << 1), + CURSOR_INVALID = (1 << 2) +} SetCursorFlags; + + /* This lovely little value is used to determine how far away from the title bar + * you can move the mouse and still have a column drag work. + */ +#define TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER(tree_view) (10*gtk_tree_view_get_effective_header_height(tree_view)) + +#ifdef __GNUC__ + +#define TREE_VIEW_INTERNAL_ASSERT(expr, ret) G_STMT_START{ \ + if (!(expr)) \ + { \ + g_log (G_LOG_DOMAIN, \ + G_LOG_LEVEL_CRITICAL, \ + "%s (%s): assertion `%s' failed.\n" \ + "There is a disparity between the internal view of the GtkTreeView,\n" \ + "and the GtkTreeModel. This generally means that the model has changed\n"\ + "without letting the view know. Any display from now on is likely to\n" \ + "be incorrect.\n", \ + G_STRLOC, \ + G_STRFUNC, \ + #expr); \ + return ret; \ + }; }G_STMT_END + +#define TREE_VIEW_INTERNAL_ASSERT_VOID(expr) G_STMT_START{ \ + if (!(expr)) \ + { \ + g_log (G_LOG_DOMAIN, \ + G_LOG_LEVEL_CRITICAL, \ + "%s (%s): assertion `%s' failed.\n" \ + "There is a disparity between the internal view of the GtkTreeView,\n" \ + "and the GtkTreeModel. This generally means that the model has changed\n"\ + "without letting the view know. Any display from now on is likely to\n" \ + "be incorrect.\n", \ + G_STRLOC, \ + G_STRFUNC, \ + #expr); \ + return; \ + }; }G_STMT_END + +#else + +#define TREE_VIEW_INTERNAL_ASSERT(expr, ret) G_STMT_START{ \ + if (!(expr)) \ + { \ + g_log (G_LOG_DOMAIN, \ + G_LOG_LEVEL_CRITICAL, \ + "file %s: line %d: assertion `%s' failed.\n" \ + "There is a disparity between the internal view of the GtkTreeView,\n" \ + "and the GtkTreeModel. This generally means that the model has changed\n"\ + "without letting the view know. Any display from now on is likely to\n" \ + "be incorrect.\n", \ + __FILE__, \ + __LINE__, \ + #expr); \ + return ret; \ + }; }G_STMT_END + +#define TREE_VIEW_INTERNAL_ASSERT_VOID(expr) G_STMT_START{ \ + if (!(expr)) \ + { \ + g_log (G_LOG_DOMAIN, \ + G_LOG_LEVEL_CRITICAL, \ + "file %s: line %d: assertion '%s' failed.\n" \ + "There is a disparity between the internal view of the GtkTreeView,\n" \ + "and the GtkTreeModel. This generally means that the model has changed\n"\ + "without letting the view know. Any display from now on is likely to\n" \ + "be incorrect.\n", \ + __FILE__, \ + __LINE__, \ + #expr); \ + return; \ + }; }G_STMT_END +#endif + +#define GTK_TREE_VIEW_PRIORITY_VALIDATE (GDK_PRIORITY_REDRAW + 5) +#define GTK_TREE_VIEW_PRIORITY_SCROLL_SYNC (GTK_TREE_VIEW_PRIORITY_VALIDATE + 2) +/* 3/5 of gdkframeclockidle.c's FRAME_INTERVAL (16667 microsecs) */ +#define GTK_TREE_VIEW_TIME_MS_PER_IDLE 10 +#define SCROLL_EDGE_SIZE 15 +#define GTK_TREE_VIEW_SEARCH_DIALOG_TIMEOUT 5000 +#define AUTO_EXPAND_TIMEOUT 500 + +/* Translate from bin_window coordinates to rbtree (tree coordinates) and + * vice versa. + */ +#define TREE_WINDOW_Y_TO_RBTREE_Y(tree_view,y) ((y) + tree_view->dy) +#define RBTREE_Y_TO_TREE_WINDOW_Y(tree_view,y) ((y) - tree_view->dy) + +/* Vertical separator width. Must be an even number. */ +#define _TREE_VIEW_VERTICAL_SEPARATOR 2 +/* Horizontal separator width. Must be an even number. */ +#define _TREE_VIEW_HORIZONTAL_SEPARATOR 4 +/* Tree view grid line width, in pixels */ +#define _TREE_VIEW_GRID_LINE_WIDTH 1 +/* Tree view line width, in pixels */ +#define _TREE_VIEW_TREE_LINE_WIDTH 1 + +typedef struct _GtkTreeViewColumnReorder GtkTreeViewColumnReorder; +struct _GtkTreeViewColumnReorder +{ + int left_align; + int right_align; + GtkTreeViewColumn *left_column; + GtkTreeViewColumn *right_column; +}; + +typedef struct _GtkTreeViewChild GtkTreeViewChild; +struct _GtkTreeViewChild +{ + GtkWidget *widget; + GtkTreeRBNode *node; + GtkTreeRBTree *tree; + GtkTreeViewColumn *column; + GtkBorder border; +}; + + +typedef struct _TreeViewDragInfo TreeViewDragInfo; +struct _TreeViewDragInfo +{ + GdkContentFormats *source_formats; + GdkDragAction source_actions; + GdkDrag *drag; + GtkTreeRowReference *source_item; + + GtkCssNode *cssnode; + GtkDropTargetAsync *dest; + GdkModifierType start_button_mask; + + guint source_set : 1; + guint dest_set : 1; +}; + + +typedef struct +{ + GtkTreeModel *model; + + /* tree information */ + GtkTreeRBTree *tree; + + /* Container info */ + GList *children; + int width; + + guint presize_handler_tick_cb; + + /* Adjustments */ + GtkAdjustment *hadjustment; + GtkAdjustment *vadjustment; + int min_display_width; + int min_display_height; + + /* CSS nodes */ + GtkCssNode *header_node; + + /* Scroll position state keeping */ + GtkTreeRowReference *top_row; + int top_row_dy; + /* dy == y pos of top_row + top_row_dy */ + /* we cache it for simplicity of the code */ + int dy; + + guint validate_rows_timer; + guint scroll_sync_timer; + + /* Indentation and expander layout */ + GtkTreeViewColumn *expander_column; + + int level_indentation; + + /* Key navigation (focus), selection */ + int cursor_offset; + + GtkTreeRowReference *anchor; + GtkTreeRBNode *cursor_node; + GtkTreeRBTree *cursor_tree; + + GtkTreeViewColumn *focus_column; + + /* Current pressed node, previously pressed, prelight */ + GtkTreeRBNode *button_pressed_node; + GtkTreeRBTree *button_pressed_tree; + + int press_start_x; + int press_start_y; + + int event_last_x; + int event_last_y; + + GtkTreeRBNode *prelight_node; + GtkTreeRBTree *prelight_tree; + + /* Cell Editing */ + GtkTreeViewColumn *edited_column; + + /* Auto expand/collapse timeout in hover mode */ + guint auto_expand_timeout; + + /* Selection information */ + GtkTreeSelection *selection; + + /* Header information */ + int header_height; + int n_columns; + GList *columns; + + GtkTreeViewColumnDropFunc column_drop_func; + gpointer column_drop_func_data; + GDestroyNotify column_drop_func_data_destroy; + GList *column_drag_info; + GtkTreeViewColumnReorder *cur_reorder; + + int prev_width_before_expander; + + /* Scroll timeout (e.g. during dnd, rubber banding) */ + guint scroll_timeout; + + /* Interactive Header reordering */ + GtkTreeViewColumn *drag_column; + int drag_column_x; + int drag_column_y; + + /* Interactive Header Resizing */ + int drag_pos; + int x_drag; + + /* Row drag-and-drop */ + GtkTreeRowReference *drag_dest_row; + GtkTreeViewDropPosition drag_dest_pos; + guint open_dest_timeout; + + /* Rubber banding */ + int rubber_band_status; + int rubber_band_x; + int rubber_band_y; + int rubber_band_extend; + int rubber_band_modify; + + /* fixed height */ + int fixed_height; + + GtkTreeRBNode *rubber_band_start_node; + GtkTreeRBTree *rubber_band_start_tree; + + GtkTreeRBNode *rubber_band_end_node; + GtkTreeRBTree *rubber_band_end_tree; + GtkCssNode *rubber_band_cssnode; + + /* Scroll-to functionality when unrealized */ + GtkTreeRowReference *scroll_to_path; + GtkTreeViewColumn *scroll_to_column; + float scroll_to_row_align; + float scroll_to_col_align; + + /* Interactive search */ + int selected_iter; + int search_column; + GtkTreeViewSearchEqualFunc search_equal_func; + gpointer search_user_data; + GDestroyNotify search_destroy; + gpointer search_position_user_data; + GDestroyNotify search_position_destroy; + GtkWidget *search_popover; + GtkWidget *search_entry; + gulong search_entry_changed_id; + guint typeselect_flush_timeout; + + /* Grid and tree lines */ + GtkTreeViewGridLines grid_lines; + gboolean tree_lines_enabled; + + /* Row separators */ + GtkTreeViewRowSeparatorFunc row_separator_func; + gpointer row_separator_data; + GDestroyNotify row_separator_destroy; + + /* Gestures */ + GtkGesture *click_gesture; + GtkGesture *drag_gesture; /* Rubberbanding, row DnD */ + GtkGesture *column_drag_gesture; /* Column reordering, resizing */ + + /* Tooltip support */ + int tooltip_column; + + int expander_size; + + GdkRGBA grid_line_color; /* Color used in the textures */ + GdkTexture *horizontal_grid_line_texture; + GdkTexture *vertical_grid_line_texture; + + GdkRGBA tree_line_color; /* Color used in the textures */ + GdkTexture *horizontal_tree_line_texture; + GdkTexture *vertical_tree_line_texture; + + /* Here comes the bitfield */ + guint scroll_to_use_align : 1; + + guint fixed_height_mode : 1; + guint fixed_height_check : 1; + + guint activate_on_single_click : 1; + guint reorderable : 1; + guint header_has_focus : 1; + guint drag_column_surface_state : 3; + guint mark_rows_col_dirty : 1; + + /* for DnD */ + guint empty_view_drop : 1; + + guint modify_selection_pressed : 1; + guint extend_selection_pressed : 1; + + guint in_top_row_to_dy : 1; + + /* interactive search */ + guint enable_search : 1; + guint disable_popdown : 1; + guint search_custom_entry_set : 1; + + guint hover_selection : 1; + guint hover_expand : 1; + guint imcontext_changed : 1; + + guint in_scroll : 1; + + guint rubber_banding_enable : 1; + + guint in_grab : 1; + + /* Whether our key press handler is to avoid sending an unhandled binding to the search entry */ + guint search_entry_avoid_unhandled_binding : 1; + + /* GtkScrollablePolicy needs to be checked when + * driving the scrollable adjustment values */ + guint hscroll_policy : 1; + guint vscroll_policy : 1; + + /* GtkTreeView flags */ + guint is_list : 1; + guint show_expanders : 1; + guint in_column_resize : 1; + guint arrow_prelit : 1; + guint headers_visible : 1; + guint draw_keyfocus : 1; + guint model_setup : 1; + guint in_column_drag : 1; +} GtkTreeViewPrivate; + + +/* Signals */ +enum +{ + ROW_ACTIVATED, + TEST_EXPAND_ROW, + TEST_COLLAPSE_ROW, + ROW_EXPANDED, + ROW_COLLAPSED, + COLUMNS_CHANGED, + CURSOR_CHANGED, + MOVE_CURSOR, + SELECT_ALL, + UNSELECT_ALL, + SELECT_CURSOR_ROW, + TOGGLE_CURSOR_ROW, + EXPAND_COLLAPSE_CURSOR_ROW, + SELECT_CURSOR_PARENT, + START_INTERACTIVE_SEARCH, + LAST_SIGNAL +}; + +/* Properties */ +enum { + PROP_0, + PROP_MODEL, + PROP_HEADERS_VISIBLE, + PROP_HEADERS_CLICKABLE, + PROP_EXPANDER_COLUMN, + PROP_REORDERABLE, + PROP_ENABLE_SEARCH, + PROP_SEARCH_COLUMN, + PROP_FIXED_HEIGHT_MODE, + PROP_HOVER_SELECTION, + PROP_HOVER_EXPAND, + PROP_SHOW_EXPANDERS, + PROP_LEVEL_INDENTATION, + PROP_RUBBER_BANDING, + PROP_ENABLE_GRID_LINES, + PROP_ENABLE_TREE_LINES, + PROP_TOOLTIP_COLUMN, + PROP_ACTIVATE_ON_SINGLE_CLICK, + LAST_PROP, + /* overridden */ + PROP_HADJUSTMENT = LAST_PROP, + PROP_VADJUSTMENT, + PROP_HSCROLL_POLICY, + PROP_VSCROLL_POLICY, +}; + +/* object signals */ +static void gtk_tree_view_finalize (GObject *object); +static void gtk_tree_view_dispose (GObject *object); +static void gtk_tree_view_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gtk_tree_view_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); + +/* gtkwidget signals */ +static void gtk_tree_view_realize (GtkWidget *widget); +static void gtk_tree_view_unrealize (GtkWidget *widget); +static void gtk_tree_view_unroot (GtkWidget *widget); +static void gtk_tree_view_map (GtkWidget *widget); +static void gtk_tree_view_measure (GtkWidget *widget, + GtkOrientation orientation, + int for_size, + int *minimum, + int *natural, + int *minimum_baseline, + int *natural_baseline); +static void gtk_tree_view_size_allocate (GtkWidget *widget, + int width, + int height, + int baseline); +static void gtk_tree_view_snapshot (GtkWidget *widget, + GtkSnapshot *snapshot); + +static gboolean gtk_tree_view_forward_controller_key_pressed (GtkEventControllerKey *key, + guint keyval, + guint keycode, + GdkModifierType state, + GtkTreeView *tree_view); +static gboolean gtk_tree_view_key_controller_key_pressed (GtkEventControllerKey *key, + guint keyval, + guint keycode, + GdkModifierType state, + GtkTreeView *tree_view); +static void gtk_tree_view_key_controller_key_released (GtkEventControllerKey *key, + guint keyval, + guint keycode, + GdkModifierType state, + GtkTreeView *tree_view); +static void gtk_tree_view_focus_controller_focus_out (GtkEventController *focus, + GtkTreeView *tree_view); + +static int gtk_tree_view_focus (GtkWidget *widget, + GtkDirectionType direction); +static gboolean gtk_tree_view_grab_focus (GtkWidget *widget); +static void gtk_tree_view_css_changed (GtkWidget *widget, + GtkCssStyleChange *change); + +static void gtk_tree_view_remove (GtkTreeView *tree_view, + GtkWidget *widget); + +/* Source side drag signals */ +static void gtk_tree_view_dnd_finished_cb (GdkDrag *drag, + GtkWidget *widget); +static GdkContentProvider * gtk_tree_view_drag_data_get (GtkTreeView *tree_view, + GtkTreePath *source_row); + +/* Target side drag signals */ +static void gtk_tree_view_drag_leave (GtkDropTargetAsync *dest, + GdkDrop *drop, + GtkTreeView *tree_view); +static GdkDragAction gtk_tree_view_drag_motion (GtkDropTargetAsync *dest, + GdkDrop *drop, + double x, + double y, + GtkTreeView *tree_view); +static gboolean gtk_tree_view_drag_drop (GtkDropTargetAsync *dest, + GdkDrop *drop, + double x, + double y, + GtkTreeView *tree_view); +static void gtk_tree_view_drag_data_received (GObject *source, + GAsyncResult *result, + gpointer data); + +/* tree_model signals */ +static gboolean gtk_tree_view_real_move_cursor (GtkTreeView *tree_view, + GtkMovementStep step, + int count, + gboolean extend, + gboolean modify); +static gboolean gtk_tree_view_real_select_all (GtkTreeView *tree_view); +static gboolean gtk_tree_view_real_unselect_all (GtkTreeView *tree_view); +static gboolean gtk_tree_view_real_select_cursor_row (GtkTreeView *tree_view, + gboolean start_editing); +static gboolean gtk_tree_view_real_toggle_cursor_row (GtkTreeView *tree_view); +static gboolean gtk_tree_view_real_expand_collapse_cursor_row (GtkTreeView *tree_view, + gboolean logical, + gboolean expand, + gboolean open_all); +static gboolean gtk_tree_view_real_select_cursor_parent (GtkTreeView *tree_view); +static void gtk_tree_view_row_changed (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data); +static void gtk_tree_view_row_inserted (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data); +static void gtk_tree_view_row_has_child_toggled (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data); +static void gtk_tree_view_row_deleted (GtkTreeModel *model, + GtkTreePath *path, + gpointer data); +static void gtk_tree_view_rows_reordered (GtkTreeModel *model, + GtkTreePath *parent, + GtkTreeIter *iter, + int *new_order, + gpointer data); + +/* Incremental reflow */ +static gboolean validate_row (GtkTreeView *tree_view, + GtkTreeRBTree *tree, + GtkTreeRBNode *node, + GtkTreeIter *iter, + GtkTreePath *path); +static void validate_visible_area (GtkTreeView *tree_view); +static gboolean do_validate_rows (GtkTreeView *tree_view, + gboolean queue_resize); +static gboolean validate_rows (GtkTreeView *tree_view); +static void install_presize_handler (GtkTreeView *tree_view); +static void install_scroll_sync_handler (GtkTreeView *tree_view); +static void gtk_tree_view_set_top_row (GtkTreeView *tree_view, + GtkTreePath *path, + int offset); +static void gtk_tree_view_dy_to_top_row (GtkTreeView *tree_view); +static void gtk_tree_view_top_row_to_dy (GtkTreeView *tree_view); +static void invalidate_empty_focus (GtkTreeView *tree_view); + +/* Internal functions */ +static gboolean gtk_tree_view_is_expander_column (GtkTreeView *tree_view, + GtkTreeViewColumn *column); +static inline gboolean gtk_tree_view_draw_expanders (GtkTreeView *tree_view); +static void gtk_tree_view_add_move_binding (GtkWidgetClass *widget_class, + guint keyval, + guint modmask, + gboolean add_shifted_binding, + GtkMovementStep step, + int count); +static int gtk_tree_view_unref_and_check_selection_tree (GtkTreeView *tree_view, + GtkTreeRBTree *tree); +static void gtk_tree_view_snapshot_arrow (GtkTreeView *tree_view, + GtkSnapshot *snapshot, + GtkTreeRBTree *tree, + GtkTreeRBNode *node); +static void gtk_tree_view_get_arrow_xrange (GtkTreeView *tree_view, + GtkTreeRBTree *tree, + int *x1, + int *x2); +static void gtk_tree_view_adjustment_changed (GtkAdjustment *adjustment, + GtkTreeView *tree_view); +static void gtk_tree_view_build_tree (GtkTreeView *tree_view, + GtkTreeRBTree *tree, + GtkTreeIter *iter, + int depth, + gboolean recurse); +static void gtk_tree_view_clamp_node_visible (GtkTreeView *tree_view, + GtkTreeRBTree *tree, + GtkTreeRBNode *node); +static void gtk_tree_view_clamp_column_visible (GtkTreeView *tree_view, + GtkTreeViewColumn *column, + gboolean focus_to_cell); +static gboolean gtk_tree_view_maybe_begin_dragging_row (GtkTreeView *tree_view); +static void gtk_tree_view_focus_to_cursor (GtkTreeView *tree_view); +static void gtk_tree_view_move_cursor_up_down (GtkTreeView *tree_view, + int count); +static void gtk_tree_view_move_cursor_page_up_down (GtkTreeView *tree_view, + int count); +static void gtk_tree_view_move_cursor_left_right (GtkTreeView *tree_view, + int count); +static void gtk_tree_view_move_cursor_start_end (GtkTreeView *tree_view, + int count); +static gboolean gtk_tree_view_real_collapse_row (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeRBTree *tree, + GtkTreeRBNode *node); +static gboolean gtk_tree_view_real_expand_row (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeRBTree *tree, + GtkTreeRBNode *node, + gboolean open_all); +static void gtk_tree_view_real_set_cursor (GtkTreeView *tree_view, + GtkTreePath *path, + SetCursorFlags flags); +static gboolean gtk_tree_view_has_can_focus_cell (GtkTreeView *tree_view); +static void column_sizing_notify (GObject *object, + GParamSpec *pspec, + gpointer data); +static void gtk_tree_view_stop_rubber_band (GtkTreeView *tree_view); +static void ensure_unprelighted (GtkTreeView *tree_view); +static void update_prelight (GtkTreeView *tree_view, + int x, + int y); + +static inline int gtk_tree_view_get_effective_header_height (GtkTreeView *tree_view); + +static inline int gtk_tree_view_get_cell_area_y_offset (GtkTreeView *tree_view, + GtkTreeRBTree *tree, + GtkTreeRBNode *node); +static inline int gtk_tree_view_get_cell_area_height (GtkTreeView *tree_view, + GtkTreeRBNode *node); + +static inline int gtk_tree_view_get_row_y_offset (GtkTreeView *tree_view, + GtkTreeRBTree *tree, + GtkTreeRBNode *node); +static inline int gtk_tree_view_get_row_height (GtkTreeView *tree_view, + GtkTreeRBNode *node); +static TreeViewDragInfo* get_info (GtkTreeView *tree_view); + +/* interactive search */ +static void gtk_tree_view_ensure_interactive_directory (GtkTreeView *tree_view); +static void gtk_tree_view_search_popover_hide (GtkWidget *search_popover, + GtkTreeView *tree_view); +static void gtk_tree_view_search_preedit_changed (GtkText *text, + const char *preedit, + GtkTreeView *tree_view); +static void gtk_tree_view_search_changed (GtkEditable *editable, + GtkTreeView *tree_view); +static void gtk_tree_view_search_activate (GtkEntry *entry, + GtkTreeView *tree_view); +static void gtk_tree_view_search_pressed_cb (GtkGesture *gesture, + int n_press, + double x, + double y, + GtkTreeView *tree_view); +static gboolean gtk_tree_view_search_scroll_event (GtkWidget *entry, + double dx, + double dy, + GtkTreeView *tree_view); +static gboolean gtk_tree_view_search_key_pressed (GtkEventControllerKey *key, + guint keyval, + guint keycode, + GdkModifierType state, + GtkTreeView *tree_view); +static gboolean gtk_tree_view_search_move (GtkWidget *window, + GtkTreeView *tree_view, + gboolean up); +static gboolean gtk_tree_view_search_equal_func (GtkTreeModel *model, + int column, + const char *key, + GtkTreeIter *iter, + gpointer search_data); +static gboolean gtk_tree_view_search_iter (GtkTreeModel *model, + GtkTreeSelection *selection, + GtkTreeIter *iter, + const char *text, + int *count, + int n); +static void gtk_tree_view_search_init (GtkWidget *entry, + GtkTreeView *tree_view); +static void gtk_tree_view_put (GtkTreeView *tree_view, + GtkWidget *child_widget, + GtkTreePath *path, + GtkTreeViewColumn*column, + const GtkBorder *border); +static gboolean gtk_tree_view_start_editing (GtkTreeView *tree_view, + GtkTreePath *cursor_path, + gboolean edit_only); +static void gtk_tree_view_stop_editing (GtkTreeView *tree_view, + gboolean cancel_editing); +static gboolean gtk_tree_view_real_start_interactive_search (GtkTreeView *tree_view, + gboolean keybinding); +static gboolean gtk_tree_view_start_interactive_search (GtkTreeView *tree_view); +static GtkTreeViewColumn *gtk_tree_view_get_drop_column (GtkTreeView *tree_view, + GtkTreeViewColumn *column, + int drop_position); + +/* GtkBuildable */ +static void gtk_tree_view_buildable_add_child (GtkBuildable *tree_view, + GtkBuilder *builder, + GObject *child, + const char *type); +static GObject *gtk_tree_view_buildable_get_internal_child (GtkBuildable *buildable, + GtkBuilder *builder, + const char *childname); +static void gtk_tree_view_buildable_init (GtkBuildableIface *iface); + +/* GtkScrollable */ +static void gtk_tree_view_scrollable_init (GtkScrollableInterface *iface); + +static void gtk_tree_view_do_set_hadjustment (GtkTreeView *tree_view, + GtkAdjustment *adjustment); +static void gtk_tree_view_do_set_vadjustment (GtkTreeView *tree_view, + GtkAdjustment *adjustment); + +static gboolean scroll_row_timeout (gpointer data); +static void add_scroll_timeout (GtkTreeView *tree_view); +static void remove_scroll_timeout (GtkTreeView *tree_view); + +static void grab_focus_and_unset_draw_keyfocus (GtkTreeView *tree_view); + +/* Gestures */ +static void gtk_tree_view_column_click_gesture_pressed (GtkGestureClick *gesture, + int n_press, + double x, + double y, + GtkTreeView *tree_view); + +static void gtk_tree_view_click_gesture_pressed (GtkGestureClick *gesture, + int n_press, + double x, + double y, + GtkTreeView *tree_view); +static void gtk_tree_view_click_gesture_released (GtkGestureClick *gesture, + int n_press, + double x, + double y, + GtkTreeView *tree_view); + +static void gtk_tree_view_column_drag_gesture_begin (GtkGestureDrag *gesture, + double start_x, + double start_y, + GtkTreeView *tree_view); +static void gtk_tree_view_column_drag_gesture_update (GtkGestureDrag *gesture, + double offset_x, + double offset_y, + GtkTreeView *tree_view); +static void gtk_tree_view_column_drag_gesture_end (GtkGestureDrag *gesture, + double offset_x, + double offset_y, + GtkTreeView *tree_view); + +static void gtk_tree_view_drag_gesture_begin (GtkGestureDrag *gesture, + double start_x, + double start_y, + GtkTreeView *tree_view); +static void gtk_tree_view_drag_gesture_update (GtkGestureDrag *gesture, + double offset_x, + double offset_y, + GtkTreeView *tree_view); +static void gtk_tree_view_drag_gesture_end (GtkGestureDrag *gesture, + double offset_x, + double offset_y, + GtkTreeView *tree_view); +static void gtk_tree_view_motion_controller_enter (GtkEventControllerMotion *controller, + double x, + double y, + GtkTreeView *tree_view); +static void gtk_tree_view_motion_controller_leave (GtkEventControllerMotion *controller, + GtkTreeView *tree_view); +static void gtk_tree_view_motion_controller_motion (GtkEventControllerMotion *controller, + double x, + double y, + GtkTreeView *tree_view); + +static guint tree_view_signals [LAST_SIGNAL] = { 0 }; +static GParamSpec *tree_view_props [LAST_PROP] = { NULL }; + + + +/* GType Methods + */ + +G_DEFINE_TYPE_WITH_CODE (GtkTreeView, gtk_tree_view, GTK_TYPE_WIDGET, + G_ADD_PRIVATE (GtkTreeView) + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, + gtk_tree_view_buildable_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, + gtk_tree_view_scrollable_init)) + +static void +gtk_tree_view_class_init (GtkTreeViewClass *class) +{ + GObjectClass *o_class = G_OBJECT_CLASS (class); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); + + /* GObject signals */ + o_class->set_property = gtk_tree_view_set_property; + o_class->get_property = gtk_tree_view_get_property; + o_class->finalize = gtk_tree_view_finalize; + o_class->dispose = gtk_tree_view_dispose; + + /* GtkWidget signals */ + widget_class->map = gtk_tree_view_map; + widget_class->realize = gtk_tree_view_realize; + widget_class->unrealize = gtk_tree_view_unrealize; + widget_class->unroot = gtk_tree_view_unroot; + widget_class->measure = gtk_tree_view_measure; + widget_class->size_allocate = gtk_tree_view_size_allocate; + widget_class->snapshot = gtk_tree_view_snapshot; + widget_class->focus = gtk_tree_view_focus; + widget_class->grab_focus = gtk_tree_view_grab_focus; + widget_class->css_changed = gtk_tree_view_css_changed; + + class->move_cursor = gtk_tree_view_real_move_cursor; + class->select_all = gtk_tree_view_real_select_all; + class->unselect_all = gtk_tree_view_real_unselect_all; + class->select_cursor_row = gtk_tree_view_real_select_cursor_row; + class->toggle_cursor_row = gtk_tree_view_real_toggle_cursor_row; + class->expand_collapse_cursor_row = gtk_tree_view_real_expand_collapse_cursor_row; + class->select_cursor_parent = gtk_tree_view_real_select_cursor_parent; + class->start_interactive_search = gtk_tree_view_start_interactive_search; + + /* Properties */ + + g_object_class_override_property (o_class, PROP_HADJUSTMENT, "hadjustment"); + g_object_class_override_property (o_class, PROP_VADJUSTMENT, "vadjustment"); + g_object_class_override_property (o_class, PROP_HSCROLL_POLICY, "hscroll-policy"); + g_object_class_override_property (o_class, PROP_VSCROLL_POLICY, "vscroll-policy"); + + tree_view_props[PROP_MODEL] = + g_param_spec_object ("model", NULL, NULL, + GTK_TYPE_TREE_MODEL, + GTK_PARAM_READWRITE); + + tree_view_props[PROP_HEADERS_VISIBLE] = + g_param_spec_boolean ("headers-visible", NULL, NULL, + TRUE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + + tree_view_props[PROP_HEADERS_CLICKABLE] = + g_param_spec_boolean ("headers-clickable", NULL, NULL, + TRUE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + + tree_view_props[PROP_EXPANDER_COLUMN] = + g_param_spec_object ("expander-column", NULL, NULL, + GTK_TYPE_TREE_VIEW_COLUMN, + GTK_PARAM_READWRITE); + + tree_view_props[PROP_REORDERABLE] = + g_param_spec_boolean ("reorderable", NULL, NULL, + FALSE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + + tree_view_props[PROP_ENABLE_SEARCH] = + g_param_spec_boolean ("enable-search", NULL, NULL, + TRUE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + + tree_view_props[PROP_SEARCH_COLUMN] = + g_param_spec_int ("search-column", NULL, NULL, + -1, G_MAXINT, + -1, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + + /** + * GtkTreeView:fixed-height-mode: + * + * Setting the ::fixed-height-mode property to %TRUE speeds up + * `GtkTreeView` by assuming that all rows have the same height. + * Only enable this option if all rows are the same height. + * Please see gtk_tree_view_set_fixed_height_mode() for more + * information on this option. + */ + tree_view_props[PROP_FIXED_HEIGHT_MODE] = + g_param_spec_boolean ("fixed-height-mode", NULL, NULL, + FALSE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + + /** + * GtkTreeView:hover-selection: + * + * Enables or disables the hover selection mode of @tree_view. + * Hover selection makes the selected row follow the pointer. + * Currently, this works only for the selection modes + * %GTK_SELECTION_SINGLE and %GTK_SELECTION_BROWSE. + * + * This mode is primarily intended for treeviews in popups, e.g. + * in `GtkComboBox` or `GtkEntryCompletion`. + */ + tree_view_props[PROP_HOVER_SELECTION] = + g_param_spec_boolean ("hover-selection", NULL, NULL, + FALSE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + + /** + * GtkTreeView:hover-expand: + * + * Enables or disables the hover expansion mode of @tree_view. + * Hover expansion makes rows expand or collapse if the pointer moves + * over them. + * + * This mode is primarily intended for treeviews in popups, e.g. + * in `GtkComboBox` or `GtkEntryCompletion`. + */ + tree_view_props[PROP_HOVER_EXPAND] = + g_param_spec_boolean ("hover-expand", NULL, NULL, + FALSE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + + /** + * GtkTreeView:show-expanders: + * + * %TRUE if the view has expanders. + */ + tree_view_props[PROP_SHOW_EXPANDERS] = + g_param_spec_boolean ("show-expanders", NULL, NULL, + TRUE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + + /** + * GtkTreeView:level-indentation: + * + * Extra indentation for each level. + */ + tree_view_props[PROP_LEVEL_INDENTATION] = + g_param_spec_int ("level-indentation", NULL, NULL, + 0, G_MAXINT, + 0, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + + tree_view_props[PROP_RUBBER_BANDING] = + g_param_spec_boolean ("rubber-banding", NULL, NULL, + FALSE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + + tree_view_props[PROP_ENABLE_GRID_LINES] = + g_param_spec_enum ("enable-grid-lines", NULL, NULL, + GTK_TYPE_TREE_VIEW_GRID_LINES, + GTK_TREE_VIEW_GRID_LINES_NONE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + + tree_view_props[PROP_ENABLE_TREE_LINES] = + g_param_spec_boolean ("enable-tree-lines", NULL, NULL, + FALSE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + + tree_view_props[PROP_TOOLTIP_COLUMN] = + g_param_spec_int ("tooltip-column", NULL, NULL, + -1, G_MAXINT, + -1, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + + /** + * GtkTreeView:activate-on-single-click: + * + * The activate-on-single-click property specifies whether the "row-activated" signal + * will be emitted after a single click. + */ + tree_view_props[PROP_ACTIVATE_ON_SINGLE_CLICK] = + g_param_spec_boolean ("activate-on-single-click", NULL, NULL, + FALSE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + + g_object_class_install_properties (o_class, LAST_PROP, tree_view_props); + + /* Signals */ + /** + * GtkTreeView::row-activated: + * @tree_view: the object on which the signal is emitted + * @path: the `GtkTreePath` for the activated row + * @column: (nullable): the `GtkTreeViewColumn` in which the activation occurred + * + * The "row-activated" signal is emitted when the method + * [`method@Gtk.TreeView.row_activated`] is called. + * + * This signal is emitted when the user double-clicks a treeview row with the + * [property@Gtk.TreeView:activate-on-single-click] property set to %FALSE, + * or when the user single-clicks a row when that property set to %TRUE. + * + * This signal is also emitted when a non-editable row is selected and one + * of the keys: Space, Shift+Space, + * Return or Enter is pressed. + * + * For selection handling refer to the + * [tree widget conceptual overview](section-tree-widget.html) + * as well as `GtkTreeSelection`. + */ + tree_view_signals[ROW_ACTIVATED] = + g_signal_new (I_("row-activated"), + G_TYPE_FROM_CLASS (o_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkTreeViewClass, row_activated), + NULL, NULL, + _gtk_marshal_VOID__BOXED_OBJECT, + G_TYPE_NONE, 2, + GTK_TYPE_TREE_PATH, + GTK_TYPE_TREE_VIEW_COLUMN); + g_signal_set_va_marshaller (tree_view_signals[ROW_ACTIVATED], + G_TYPE_FROM_CLASS (o_class), + _gtk_marshal_VOID__BOXED_OBJECTv); + + /** + * GtkTreeView::test-expand-row: + * @tree_view: the object on which the signal is emitted + * @iter: the tree iter of the row to expand + * @path: a tree path that points to the row + * + * The given row is about to be expanded (show its children nodes). Use this + * signal if you need to control the expandability of individual rows. + * + * Returns: %FALSE to allow expansion, %TRUE to reject + */ + tree_view_signals[TEST_EXPAND_ROW] = + g_signal_new (I_("test-expand-row"), + G_TYPE_FROM_CLASS (o_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkTreeViewClass, test_expand_row), + _gtk_boolean_handled_accumulator, NULL, + _gtk_marshal_BOOLEAN__BOXED_BOXED, + G_TYPE_BOOLEAN, 2, + GTK_TYPE_TREE_ITER, + GTK_TYPE_TREE_PATH); + g_signal_set_va_marshaller (tree_view_signals[TEST_EXPAND_ROW], + G_TYPE_FROM_CLASS (o_class), + _gtk_marshal_BOOLEAN__BOXED_BOXEDv); + + /** + * GtkTreeView::test-collapse-row: + * @tree_view: the object on which the signal is emitted + * @iter: the tree iter of the row to collapse + * @path: a tree path that points to the row + * + * The given row is about to be collapsed (hide its children nodes). Use this + * signal if you need to control the collapsibility of individual rows. + * + * Returns: %FALSE to allow collapsing, %TRUE to reject + */ + tree_view_signals[TEST_COLLAPSE_ROW] = + g_signal_new (I_("test-collapse-row"), + G_TYPE_FROM_CLASS (o_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkTreeViewClass, test_collapse_row), + _gtk_boolean_handled_accumulator, NULL, + _gtk_marshal_BOOLEAN__BOXED_BOXED, + G_TYPE_BOOLEAN, 2, + GTK_TYPE_TREE_ITER, + GTK_TYPE_TREE_PATH); + g_signal_set_va_marshaller (tree_view_signals[TEST_COLLAPSE_ROW], + G_TYPE_FROM_CLASS (o_class), + _gtk_marshal_BOOLEAN__BOXED_BOXEDv); + + /** + * GtkTreeView::row-expanded: + * @tree_view: the object on which the signal is emitted + * @iter: the tree iter of the expanded row + * @path: a tree path that points to the row + * + * The given row has been expanded (child nodes are shown). + */ + tree_view_signals[ROW_EXPANDED] = + g_signal_new (I_("row-expanded"), + G_TYPE_FROM_CLASS (o_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkTreeViewClass, row_expanded), + NULL, NULL, + _gtk_marshal_VOID__BOXED_BOXED, + G_TYPE_NONE, 2, + GTK_TYPE_TREE_ITER, + GTK_TYPE_TREE_PATH); + g_signal_set_va_marshaller (tree_view_signals[ROW_EXPANDED], + G_TYPE_FROM_CLASS (o_class), + _gtk_marshal_VOID__BOXED_BOXEDv); + + /** + * GtkTreeView::row-collapsed: + * @tree_view: the object on which the signal is emitted + * @iter: the tree iter of the collapsed row + * @path: a tree path that points to the row + * + * The given row has been collapsed (child nodes are hidden). + */ + tree_view_signals[ROW_COLLAPSED] = + g_signal_new (I_("row-collapsed"), + G_TYPE_FROM_CLASS (o_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkTreeViewClass, row_collapsed), + NULL, NULL, + _gtk_marshal_VOID__BOXED_BOXED, + G_TYPE_NONE, 2, + GTK_TYPE_TREE_ITER, + GTK_TYPE_TREE_PATH); + g_signal_set_va_marshaller (tree_view_signals[ROW_COLLAPSED], + G_TYPE_FROM_CLASS (o_class), + _gtk_marshal_VOID__BOXED_BOXEDv); + + /** + * GtkTreeView::columns-changed: + * @tree_view: the object on which the signal is emitted + * + * The number of columns of the treeview has changed. + */ + tree_view_signals[COLUMNS_CHANGED] = + g_signal_new (I_("columns-changed"), + G_TYPE_FROM_CLASS (o_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkTreeViewClass, columns_changed), + NULL, NULL, + NULL, + G_TYPE_NONE, 0); + + /** + * GtkTreeView::cursor-changed: + * @tree_view: the object on which the signal is emitted + * + * The position of the cursor (focused cell) has changed. + */ + tree_view_signals[CURSOR_CHANGED] = + g_signal_new (I_("cursor-changed"), + G_TYPE_FROM_CLASS (o_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkTreeViewClass, cursor_changed), + NULL, NULL, + NULL, + G_TYPE_NONE, 0); + + /** + * GtkTreeView::move-cursor: + * @tree_view: the object on which the signal is emitted. + * @step: the granularity of the move, as a `GtkMovementStep`. + * %GTK_MOVEMENT_LOGICAL_POSITIONS, %GTK_MOVEMENT_VISUAL_POSITIONS, + * %GTK_MOVEMENT_DISPLAY_LINES, %GTK_MOVEMENT_PAGES and + * %GTK_MOVEMENT_BUFFER_ENDS are supported. + * %GTK_MOVEMENT_LOGICAL_POSITIONS and %GTK_MOVEMENT_VISUAL_POSITIONS + * are treated identically. + * @direction: the direction to move: +1 to move forwards; -1 to move + * backwards. The resulting movement is undefined for all other values. + * @extend: whether to extend the selection + * @modify: whether to modify the selection + * + * The `GtkTreeView`::move-cursor signal is a [keybinding + * signal][class@Gtk.SignalAction] which gets emitted when the user + * presses one of the cursor keys. + * + * Applications should not connect to it, but may emit it with + * g_signal_emit_by_name() if they need to control the cursor + * programmatically. In contrast to gtk_tree_view_set_cursor() and + * gtk_tree_view_set_cursor_on_cell() when moving horizontally + * `GtkTreeView`::move-cursor does not reset the current selection. + * + * Returns: %TRUE if @step is supported, %FALSE otherwise. + */ + tree_view_signals[MOVE_CURSOR] = + g_signal_new (I_("move-cursor"), + G_TYPE_FROM_CLASS (o_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkTreeViewClass, move_cursor), + NULL, NULL, + _gtk_marshal_BOOLEAN__ENUM_INT_BOOLEAN_BOOLEAN, + G_TYPE_BOOLEAN, 4, + GTK_TYPE_MOVEMENT_STEP, + G_TYPE_INT, + G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN); + g_signal_set_va_marshaller (tree_view_signals[MOVE_CURSOR], + G_TYPE_FROM_CLASS (o_class), + _gtk_marshal_BOOLEAN__ENUM_INT_BOOLEAN_BOOLEANv); + + tree_view_signals[SELECT_ALL] = + g_signal_new (I_("select-all"), + G_TYPE_FROM_CLASS (o_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkTreeViewClass, select_all), + NULL, NULL, + _gtk_marshal_BOOLEAN__VOID, + G_TYPE_BOOLEAN, 0); + g_signal_set_va_marshaller (tree_view_signals[SELECT_ALL], + G_TYPE_FROM_CLASS (o_class), + _gtk_marshal_BOOLEAN__VOIDv); + + tree_view_signals[UNSELECT_ALL] = + g_signal_new (I_("unselect-all"), + G_TYPE_FROM_CLASS (o_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkTreeViewClass, unselect_all), + NULL, NULL, + _gtk_marshal_BOOLEAN__VOID, + G_TYPE_BOOLEAN, 0); + g_signal_set_va_marshaller (tree_view_signals[UNSELECT_ALL], + G_TYPE_FROM_CLASS (o_class), + _gtk_marshal_BOOLEAN__VOIDv); + + tree_view_signals[SELECT_CURSOR_ROW] = + g_signal_new (I_("select-cursor-row"), + G_TYPE_FROM_CLASS (o_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkTreeViewClass, select_cursor_row), + NULL, NULL, + _gtk_marshal_BOOLEAN__BOOLEAN, + G_TYPE_BOOLEAN, 1, + G_TYPE_BOOLEAN); + g_signal_set_va_marshaller (tree_view_signals[SELECT_CURSOR_ROW], + G_TYPE_FROM_CLASS (o_class), + _gtk_marshal_BOOLEAN__BOOLEANv); + + tree_view_signals[TOGGLE_CURSOR_ROW] = + g_signal_new (I_("toggle-cursor-row"), + G_TYPE_FROM_CLASS (o_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkTreeViewClass, toggle_cursor_row), + NULL, NULL, + _gtk_marshal_BOOLEAN__VOID, + G_TYPE_BOOLEAN, 0); + g_signal_set_va_marshaller (tree_view_signals[TOGGLE_CURSOR_ROW], + G_TYPE_FROM_CLASS (o_class), + _gtk_marshal_BOOLEAN__VOIDv); + + tree_view_signals[EXPAND_COLLAPSE_CURSOR_ROW] = + g_signal_new (I_("expand-collapse-cursor-row"), + G_TYPE_FROM_CLASS (o_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkTreeViewClass, expand_collapse_cursor_row), + NULL, NULL, + _gtk_marshal_BOOLEAN__BOOLEAN_BOOLEAN_BOOLEAN, + G_TYPE_BOOLEAN, 3, + G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN); + g_signal_set_va_marshaller (tree_view_signals[EXPAND_COLLAPSE_CURSOR_ROW], + G_TYPE_FROM_CLASS (o_class), + _gtk_marshal_BOOLEAN__BOOLEAN_BOOLEAN_BOOLEANv); + + tree_view_signals[SELECT_CURSOR_PARENT] = + g_signal_new (I_("select-cursor-parent"), + G_TYPE_FROM_CLASS (o_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkTreeViewClass, select_cursor_parent), + NULL, NULL, + _gtk_marshal_BOOLEAN__VOID, + G_TYPE_BOOLEAN, 0); + g_signal_set_va_marshaller (tree_view_signals[SELECT_CURSOR_PARENT], + G_TYPE_FROM_CLASS (o_class), + _gtk_marshal_BOOLEAN__VOIDv); + + tree_view_signals[START_INTERACTIVE_SEARCH] = + g_signal_new (I_("start-interactive-search"), + G_TYPE_FROM_CLASS (o_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkTreeViewClass, start_interactive_search), + NULL, NULL, + _gtk_marshal_BOOLEAN__VOID, + G_TYPE_BOOLEAN, 0); + g_signal_set_va_marshaller (tree_view_signals[START_INTERACTIVE_SEARCH], + G_TYPE_FROM_CLASS (o_class), + _gtk_marshal_BOOLEAN__VOIDv); + + /* Key bindings */ + gtk_tree_view_add_move_binding (widget_class, GDK_KEY_Up, 0, TRUE, + GTK_MOVEMENT_DISPLAY_LINES, -1); + gtk_tree_view_add_move_binding (widget_class, GDK_KEY_KP_Up, 0, TRUE, + GTK_MOVEMENT_DISPLAY_LINES, -1); + + gtk_tree_view_add_move_binding (widget_class, GDK_KEY_Down, 0, TRUE, + GTK_MOVEMENT_DISPLAY_LINES, 1); + gtk_tree_view_add_move_binding (widget_class, GDK_KEY_KP_Down, 0, TRUE, + GTK_MOVEMENT_DISPLAY_LINES, 1); + + gtk_tree_view_add_move_binding (widget_class, GDK_KEY_p, GDK_CONTROL_MASK, FALSE, + GTK_MOVEMENT_DISPLAY_LINES, -1); + + gtk_tree_view_add_move_binding (widget_class, GDK_KEY_n, GDK_CONTROL_MASK, FALSE, + GTK_MOVEMENT_DISPLAY_LINES, 1); + + gtk_tree_view_add_move_binding (widget_class, GDK_KEY_Home, 0, TRUE, + GTK_MOVEMENT_BUFFER_ENDS, -1); + gtk_tree_view_add_move_binding (widget_class, GDK_KEY_KP_Home, 0, TRUE, + GTK_MOVEMENT_BUFFER_ENDS, -1); + + gtk_tree_view_add_move_binding (widget_class, GDK_KEY_End, 0, TRUE, + GTK_MOVEMENT_BUFFER_ENDS, 1); + gtk_tree_view_add_move_binding (widget_class, GDK_KEY_KP_End, 0, TRUE, + GTK_MOVEMENT_BUFFER_ENDS, 1); + + gtk_tree_view_add_move_binding (widget_class, GDK_KEY_Page_Up, 0, TRUE, + GTK_MOVEMENT_PAGES, -1); + gtk_tree_view_add_move_binding (widget_class, GDK_KEY_KP_Page_Up, 0, TRUE, + GTK_MOVEMENT_PAGES, -1); + + gtk_tree_view_add_move_binding (widget_class, GDK_KEY_Page_Down, 0, TRUE, + GTK_MOVEMENT_PAGES, 1); + gtk_tree_view_add_move_binding (widget_class, GDK_KEY_KP_Page_Down, 0, TRUE, + GTK_MOVEMENT_PAGES, 1); + + gtk_tree_view_add_move_binding (widget_class, GDK_KEY_Right, 0, FALSE, + GTK_MOVEMENT_VISUAL_POSITIONS, 1); + gtk_tree_view_add_move_binding (widget_class, GDK_KEY_Left, 0, FALSE, + GTK_MOVEMENT_VISUAL_POSITIONS, -1); + + gtk_tree_view_add_move_binding (widget_class, GDK_KEY_KP_Right, 0, FALSE, + GTK_MOVEMENT_VISUAL_POSITIONS, 1); + gtk_tree_view_add_move_binding (widget_class, GDK_KEY_KP_Left, 0, FALSE, + GTK_MOVEMENT_VISUAL_POSITIONS, -1); + + gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_space, GDK_CONTROL_MASK, "toggle-cursor-row", NULL); + gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_KP_Space, GDK_CONTROL_MASK, "toggle-cursor-row", NULL); + + gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_a, GDK_CONTROL_MASK, "select-all", NULL); + gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_slash, GDK_CONTROL_MASK, "select-all", NULL); + + gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_A, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "unselect-all", NULL); + gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_backslash, GDK_CONTROL_MASK, "unselect-all", NULL); + + gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_space, GDK_SHIFT_MASK, "select-cursor-row", "(b)", TRUE); + gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_KP_Space, GDK_SHIFT_MASK, "select-cursor-row", "(b)", TRUE); + + gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_space, 0, "select-cursor-row", "(b)", TRUE); + gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_KP_Space, 0, "select-cursor-row", "(b)", TRUE); + gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_Return, 0, "select-cursor-row", "(b)", TRUE); + gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_ISO_Enter, 0, "select-cursor-row", "(b)", TRUE); + gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_KP_Enter, 0, "select-cursor-row", "(b)", TRUE); + + /* expand and collapse rows */ + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_plus, 0, + "expand-collapse-cursor-row", + "(bbb)", TRUE, TRUE, FALSE); + + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_asterisk, 0, + "expand-collapse-cursor-row", + "(bbb)", TRUE, TRUE, TRUE); + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_KP_Multiply, 0, + "expand-collapse-cursor-row", + "(bbb)", TRUE, TRUE, TRUE); + + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_slash, 0, + "expand-collapse-cursor-row", + "(bbb)", TRUE, FALSE, FALSE); + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_KP_Divide, 0, + "expand-collapse-cursor-row", + "(bbb)", TRUE, FALSE, FALSE); + + /* Not doable on US keyboards */ + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_plus, GDK_SHIFT_MASK, + "expand-collapse-cursor-row", + "(bbb)", TRUE, TRUE, TRUE); + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_KP_Add, 0, + "expand-collapse-cursor-row", + "(bbb)", TRUE, TRUE, FALSE); + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_KP_Add, GDK_SHIFT_MASK, + "expand-collapse-cursor-row", + "(bbb)", TRUE, TRUE, TRUE); + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_KP_Add, GDK_SHIFT_MASK, + "expand-collapse-cursor-row", + "(bbb)", TRUE, TRUE, TRUE); + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_Right, GDK_SHIFT_MASK, + "expand-collapse-cursor-row", + "(bbb)", FALSE, TRUE, TRUE); + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_KP_Right, GDK_SHIFT_MASK, + "expand-collapse-cursor-row", + "(bbb)", FALSE, TRUE, TRUE); + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_Right, GDK_CONTROL_MASK | GDK_SHIFT_MASK, + "expand-collapse-cursor-row", + "(bbb)", FALSE, TRUE, TRUE); + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_KP_Right, GDK_CONTROL_MASK | GDK_SHIFT_MASK, + "expand-collapse-cursor-row", + "(bbb)", FALSE, TRUE, TRUE); + + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_minus, 0, + "expand-collapse-cursor-row", + "(bbb)", TRUE, FALSE, FALSE); + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_minus, GDK_SHIFT_MASK, + "expand-collapse-cursor-row", + "(bbb)", TRUE, FALSE, TRUE); + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_KP_Subtract, 0, + "expand-collapse-cursor-row", + "(bbb)", TRUE, FALSE, FALSE); + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_KP_Subtract, GDK_SHIFT_MASK, + "expand-collapse-cursor-row", + "(bbb)", TRUE, FALSE, TRUE); + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_Left, GDK_SHIFT_MASK, + "expand-collapse-cursor-row", + "(bbb)", FALSE, FALSE, TRUE); + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_KP_Left, GDK_SHIFT_MASK, + "expand-collapse-cursor-row", + "(bbb)", FALSE, FALSE, TRUE); + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_Left, GDK_CONTROL_MASK | GDK_SHIFT_MASK, + "expand-collapse-cursor-row", + "(bbb)", FALSE, FALSE, TRUE); + gtk_widget_class_add_binding_signal (widget_class, + GDK_KEY_KP_Left, GDK_CONTROL_MASK | GDK_SHIFT_MASK, + "expand-collapse-cursor-row", + "(bbb)", FALSE, FALSE, TRUE); + + gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_BackSpace, 0, "select-cursor-parent", NULL); + gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_BackSpace, GDK_CONTROL_MASK, "select-cursor-parent", NULL); + + gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_f, GDK_CONTROL_MASK, "start-interactive-search", NULL); + + gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_F, GDK_CONTROL_MASK, "start-interactive-search", NULL); + + gtk_widget_class_set_css_name (widget_class, I_("treeview")); +} + +static void +gtk_tree_view_init (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkCssNode *widget_node; + GtkGesture *gesture; + GtkEventController *controller; + GtkEventController **controllers; + guint n_controllers, i; + + gtk_widget_set_overflow (GTK_WIDGET (tree_view), GTK_OVERFLOW_HIDDEN); + gtk_widget_set_focusable (GTK_WIDGET (tree_view), TRUE); + + priv->show_expanders = TRUE; + priv->draw_keyfocus = TRUE; + priv->headers_visible = TRUE; + priv->activate_on_single_click = FALSE; + + /* We need some padding */ + priv->dy = 0; + priv->cursor_offset = 0; + priv->n_columns = 0; + priv->header_height = 1; + priv->x_drag = 0; + priv->drag_pos = -1; + priv->header_has_focus = FALSE; + priv->press_start_x = -1; + priv->press_start_y = -1; + priv->reorderable = FALSE; + priv->presize_handler_tick_cb = 0; + priv->scroll_sync_timer = 0; + priv->fixed_height = -1; + priv->fixed_height_mode = FALSE; + priv->fixed_height_check = 0; + priv->selection = _gtk_tree_selection_new_with_tree_view (tree_view); + priv->enable_search = TRUE; + priv->search_column = -1; + priv->search_equal_func = gtk_tree_view_search_equal_func; + priv->search_custom_entry_set = FALSE; + priv->typeselect_flush_timeout = 0; + priv->width = 0; + priv->expander_size = -1; + + priv->hover_selection = FALSE; + priv->hover_expand = FALSE; + + priv->level_indentation = 0; + + priv->rubber_banding_enable = FALSE; + + priv->grid_lines = GTK_TREE_VIEW_GRID_LINES_NONE; + priv->tree_lines_enabled = FALSE; + + priv->tooltip_column = -1; + + priv->event_last_x = -10000; + priv->event_last_y = -10000; + + gtk_tree_view_do_set_vadjustment (tree_view, NULL); + gtk_tree_view_do_set_hadjustment (tree_view, NULL); + + gtk_widget_add_css_class (GTK_WIDGET (tree_view), "view"); + + widget_node = gtk_widget_get_css_node (GTK_WIDGET (tree_view)); + priv->header_node = gtk_css_node_new (); + gtk_css_node_set_name (priv->header_node, g_quark_from_static_string ("header")); + gtk_css_node_set_parent (priv->header_node, widget_node); + gtk_css_node_set_state (priv->header_node, gtk_css_node_get_state (widget_node)); + g_object_unref (priv->header_node); + + controller = gtk_event_controller_key_new (); + g_signal_connect (controller, "key-pressed", + G_CALLBACK (gtk_tree_view_forward_controller_key_pressed), tree_view); + gtk_widget_add_controller (GTK_WIDGET (tree_view), controller); + + controllers = gtk_widget_list_controllers (GTK_WIDGET (tree_view), GTK_PHASE_BUBBLE, &n_controllers); + for (i = 0; i < n_controllers; i ++) + { + controller = controllers[i]; + if (GTK_IS_SHORTCUT_CONTROLLER (controller)) + { + g_object_ref (controller); + gtk_widget_remove_controller (GTK_WIDGET (tree_view), controller); + gtk_widget_add_controller (GTK_WIDGET (tree_view), controller); + break; + } + } + g_free (controllers); + + priv->click_gesture = gtk_gesture_click_new (); + gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (priv->click_gesture), 0); + g_signal_connect (priv->click_gesture, "pressed", + G_CALLBACK (gtk_tree_view_click_gesture_pressed), tree_view); + g_signal_connect (priv->click_gesture, "released", + G_CALLBACK (gtk_tree_view_click_gesture_released), tree_view); + gtk_widget_add_controller (GTK_WIDGET (tree_view), GTK_EVENT_CONTROLLER (priv->click_gesture)); + + gesture = gtk_gesture_click_new (); + g_signal_connect (gesture, "pressed", + G_CALLBACK (gtk_tree_view_column_click_gesture_pressed), tree_view); + gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (gesture), + GTK_PHASE_CAPTURE); + gtk_widget_add_controller (GTK_WIDGET (tree_view), GTK_EVENT_CONTROLLER (gesture)); + + priv->drag_gesture = gtk_gesture_drag_new (); + g_signal_connect (priv->drag_gesture, "drag-begin", + G_CALLBACK (gtk_tree_view_drag_gesture_begin), tree_view); + g_signal_connect (priv->drag_gesture, "drag-update", + G_CALLBACK (gtk_tree_view_drag_gesture_update), tree_view); + g_signal_connect (priv->drag_gesture, "drag-end", + G_CALLBACK (gtk_tree_view_drag_gesture_end), tree_view); + gtk_widget_add_controller (GTK_WIDGET (tree_view), GTK_EVENT_CONTROLLER (priv->drag_gesture)); + + priv->column_drag_gesture = gtk_gesture_drag_new (); + g_signal_connect (priv->column_drag_gesture, "drag-begin", + G_CALLBACK (gtk_tree_view_column_drag_gesture_begin), tree_view); + g_signal_connect (priv->column_drag_gesture, "drag-update", + G_CALLBACK (gtk_tree_view_column_drag_gesture_update), tree_view); + g_signal_connect (priv->column_drag_gesture, "drag-end", + G_CALLBACK (gtk_tree_view_column_drag_gesture_end), tree_view); + gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->column_drag_gesture), + GTK_PHASE_CAPTURE); + gtk_widget_add_controller (GTK_WIDGET (tree_view), GTK_EVENT_CONTROLLER (priv->column_drag_gesture)); + + controller = gtk_event_controller_motion_new (); + g_signal_connect (controller, "enter", + G_CALLBACK (gtk_tree_view_motion_controller_enter), tree_view); + g_signal_connect (controller, "leave", + G_CALLBACK (gtk_tree_view_motion_controller_leave), tree_view); + g_signal_connect (controller, "motion", + G_CALLBACK (gtk_tree_view_motion_controller_motion), tree_view); + gtk_widget_add_controller (GTK_WIDGET (tree_view), controller); + + controller = gtk_event_controller_key_new (); + g_signal_connect (controller, "key-pressed", + G_CALLBACK (gtk_tree_view_key_controller_key_pressed), tree_view); + g_signal_connect (controller, "key-released", + G_CALLBACK (gtk_tree_view_key_controller_key_released), tree_view); + gtk_widget_add_controller (GTK_WIDGET (tree_view), controller); + + controller = gtk_event_controller_focus_new (); + g_signal_connect (controller, "leave", + G_CALLBACK (gtk_tree_view_focus_controller_focus_out), tree_view); + gtk_widget_add_controller (GTK_WIDGET (tree_view), controller); +} + + + +/* GObject Methods + */ + +static void +gtk_tree_view_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkTreeView *tree_view = GTK_TREE_VIEW (object); + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + switch (prop_id) + { + case PROP_MODEL: + gtk_tree_view_set_model (tree_view, g_value_get_object (value)); + break; + case PROP_HADJUSTMENT: + gtk_tree_view_do_set_hadjustment (tree_view, g_value_get_object (value)); + break; + case PROP_VADJUSTMENT: + gtk_tree_view_do_set_vadjustment (tree_view, g_value_get_object (value)); + break; + case PROP_HSCROLL_POLICY: + if (priv->hscroll_policy != g_value_get_enum (value)) + { + priv->hscroll_policy = g_value_get_enum (value); + gtk_widget_queue_resize (GTK_WIDGET (tree_view)); + g_object_notify_by_pspec (object, pspec); + } + break; + case PROP_VSCROLL_POLICY: + if (priv->vscroll_policy != g_value_get_enum (value)) + { + priv->vscroll_policy = g_value_get_enum (value); + gtk_widget_queue_resize (GTK_WIDGET (tree_view)); + g_object_notify_by_pspec (object, pspec); + } + break; + case PROP_HEADERS_VISIBLE: + gtk_tree_view_set_headers_visible (tree_view, g_value_get_boolean (value)); + break; + case PROP_HEADERS_CLICKABLE: + gtk_tree_view_set_headers_clickable (tree_view, g_value_get_boolean (value)); + break; + case PROP_EXPANDER_COLUMN: + gtk_tree_view_set_expander_column (tree_view, g_value_get_object (value)); + break; + case PROP_REORDERABLE: + gtk_tree_view_set_reorderable (tree_view, g_value_get_boolean (value)); + break; + case PROP_ENABLE_SEARCH: + gtk_tree_view_set_enable_search (tree_view, g_value_get_boolean (value)); + break; + case PROP_SEARCH_COLUMN: + gtk_tree_view_set_search_column (tree_view, g_value_get_int (value)); + break; + case PROP_FIXED_HEIGHT_MODE: + gtk_tree_view_set_fixed_height_mode (tree_view, g_value_get_boolean (value)); + break; + case PROP_HOVER_SELECTION: + if (priv->hover_selection != g_value_get_boolean (value)) + { + priv->hover_selection = g_value_get_boolean (value); + g_object_notify_by_pspec (object, pspec); + } + break; + case PROP_HOVER_EXPAND: + if (priv->hover_expand != g_value_get_boolean (value)) + { + priv->hover_expand = g_value_get_boolean (value); + g_object_notify_by_pspec (object, pspec); + } + break; + case PROP_SHOW_EXPANDERS: + gtk_tree_view_set_show_expanders (tree_view, g_value_get_boolean (value)); + break; + case PROP_LEVEL_INDENTATION: + if (priv->level_indentation != g_value_get_int (value)) + { + priv->level_indentation = g_value_get_int (value); + g_object_notify_by_pspec (object, pspec); + } + break; + case PROP_RUBBER_BANDING: + if (priv->rubber_banding_enable != g_value_get_boolean (value)) + { + priv->rubber_banding_enable = g_value_get_boolean (value); + g_object_notify_by_pspec (object, pspec); + } + break; + case PROP_ENABLE_GRID_LINES: + gtk_tree_view_set_grid_lines (tree_view, g_value_get_enum (value)); + break; + case PROP_ENABLE_TREE_LINES: + gtk_tree_view_set_enable_tree_lines (tree_view, g_value_get_boolean (value)); + break; + case PROP_TOOLTIP_COLUMN: + gtk_tree_view_set_tooltip_column (tree_view, g_value_get_int (value)); + break; + case PROP_ACTIVATE_ON_SINGLE_CLICK: + gtk_tree_view_set_activate_on_single_click (tree_view, g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_tree_view_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkTreeView *tree_view = GTK_TREE_VIEW (object); + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + switch (prop_id) + { + case PROP_MODEL: + g_value_set_object (value, priv->model); + break; + case PROP_HADJUSTMENT: + g_value_set_object (value, priv->hadjustment); + break; + case PROP_VADJUSTMENT: + g_value_set_object (value, priv->vadjustment); + break; + case PROP_HSCROLL_POLICY: + g_value_set_enum (value, priv->hscroll_policy); + break; + case PROP_VSCROLL_POLICY: + g_value_set_enum (value, priv->vscroll_policy); + break; + case PROP_HEADERS_VISIBLE: + g_value_set_boolean (value, gtk_tree_view_get_headers_visible (tree_view)); + break; + case PROP_HEADERS_CLICKABLE: + g_value_set_boolean (value, gtk_tree_view_get_headers_clickable (tree_view)); + break; + case PROP_EXPANDER_COLUMN: + g_value_set_object (value, priv->expander_column); + break; + case PROP_REORDERABLE: + g_value_set_boolean (value, priv->reorderable); + break; + case PROP_ENABLE_SEARCH: + g_value_set_boolean (value, priv->enable_search); + break; + case PROP_SEARCH_COLUMN: + g_value_set_int (value, priv->search_column); + break; + case PROP_FIXED_HEIGHT_MODE: + g_value_set_boolean (value, priv->fixed_height_mode); + break; + case PROP_HOVER_SELECTION: + g_value_set_boolean (value, priv->hover_selection); + break; + case PROP_HOVER_EXPAND: + g_value_set_boolean (value, priv->hover_expand); + break; + case PROP_SHOW_EXPANDERS: + g_value_set_boolean (value, priv->show_expanders); + break; + case PROP_LEVEL_INDENTATION: + g_value_set_int (value, priv->level_indentation); + break; + case PROP_RUBBER_BANDING: + g_value_set_boolean (value, priv->rubber_banding_enable); + break; + case PROP_ENABLE_GRID_LINES: + g_value_set_enum (value, priv->grid_lines); + break; + case PROP_ENABLE_TREE_LINES: + g_value_set_boolean (value, priv->tree_lines_enabled); + break; + case PROP_TOOLTIP_COLUMN: + g_value_set_int (value, priv->tooltip_column); + break; + case PROP_ACTIVATE_ON_SINGLE_CLICK: + g_value_set_boolean (value, priv->activate_on_single_click); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_tree_view_finalize (GObject *object) +{ + G_OBJECT_CLASS (gtk_tree_view_parent_class)->finalize (object); +} + + +static GtkBuildableIface *parent_buildable_iface; + +static void +gtk_tree_view_buildable_init (GtkBuildableIface *iface) +{ + parent_buildable_iface = g_type_interface_peek_parent (iface); + iface->add_child = gtk_tree_view_buildable_add_child; + iface->get_internal_child = gtk_tree_view_buildable_get_internal_child; +} + +static void +gtk_tree_view_buildable_add_child (GtkBuildable *tree_view, + GtkBuilder *builder, + GObject *child, + const char *type) +{ + if (GTK_IS_TREE_VIEW_COLUMN (child)) + gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), GTK_TREE_VIEW_COLUMN (child)); + else + parent_buildable_iface->add_child (tree_view, builder, child, type); +} + +static GObject * +gtk_tree_view_buildable_get_internal_child (GtkBuildable *buildable, + GtkBuilder *builder, + const char *childname) +{ + GtkTreeView *tree_view = GTK_TREE_VIEW (buildable); + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + if (strcmp (childname, "selection") == 0) + return G_OBJECT (priv->selection); + + return parent_buildable_iface->get_internal_child (buildable, + builder, + childname); +} + +/* GtkWidget Methods + */ + +static void +gtk_tree_view_free_rbtree (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + gtk_tree_rbtree_free (priv->tree); + + priv->tree = NULL; + priv->button_pressed_node = NULL; + priv->button_pressed_tree = NULL; + priv->prelight_tree = NULL; + priv->prelight_node = NULL; +} + +static void +gtk_tree_view_destroy_search_popover (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + gtk_widget_unparent (priv->search_popover); + + priv->search_popover = NULL; + priv->search_entry = NULL; + priv->search_entry_changed_id = 0; +} + +static void +gtk_tree_view_dispose (GObject *object) +{ + GtkTreeView *tree_view = GTK_TREE_VIEW (object); + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GList *list; + + gtk_tree_view_stop_editing (tree_view, TRUE); + gtk_tree_view_stop_rubber_band (tree_view); + + if (priv->columns != NULL) + { + list = priv->columns; + while (list) + { + GtkTreeViewColumn *column; + column = GTK_TREE_VIEW_COLUMN (list->data); + list = list->next; + gtk_tree_view_remove_column (tree_view, column); + } + priv->columns = NULL; + } + + if (priv->tree != NULL) + { + gtk_tree_view_unref_and_check_selection_tree (tree_view, priv->tree); + + gtk_tree_view_free_rbtree (tree_view); + } + + if (priv->selection != NULL) + { + _gtk_tree_selection_set_tree_view (priv->selection, NULL); + g_object_unref (priv->selection); + priv->selection = NULL; + } + + g_clear_pointer (&priv->scroll_to_path, gtk_tree_row_reference_free); + g_clear_pointer (&priv->drag_dest_row, gtk_tree_row_reference_free); + g_clear_pointer (&priv->top_row, gtk_tree_row_reference_free); + + if (priv->column_drop_func_data && + priv->column_drop_func_data_destroy) + { + priv->column_drop_func_data_destroy (priv->column_drop_func_data); + priv->column_drop_func_data = NULL; + } + + gtk_tree_row_reference_free (priv->anchor); + priv->anchor = NULL; + + /* destroy interactive search dialog */ + if (priv->search_popover) + { + gtk_tree_view_destroy_search_popover (tree_view); + if (priv->typeselect_flush_timeout) + { + g_source_remove (priv->typeselect_flush_timeout); + priv->typeselect_flush_timeout = 0; + } + } + + if (priv->search_custom_entry_set) + { + GtkEventController *controller; + + g_signal_handlers_disconnect_by_func (priv->search_entry, + G_CALLBACK (gtk_tree_view_search_init), + tree_view); + + if (GTK_IS_ENTRY (priv->search_entry)) + controller = gtk_entry_get_key_controller (GTK_ENTRY (priv->search_entry)); + else + controller = gtk_search_entry_get_key_controller (GTK_SEARCH_ENTRY (priv->search_entry)); + g_signal_handlers_disconnect_by_func (controller, + G_CALLBACK (gtk_tree_view_search_key_pressed), + tree_view); + + g_object_unref (priv->search_entry); + + priv->search_entry = NULL; + priv->search_custom_entry_set = FALSE; + } + + if (priv->search_destroy && priv->search_user_data) + { + priv->search_destroy (priv->search_user_data); + priv->search_user_data = NULL; + } + + if (priv->search_position_destroy && priv->search_position_user_data) + { + priv->search_position_destroy (priv->search_position_user_data); + priv->search_position_user_data = NULL; + } + + if (priv->row_separator_destroy && priv->row_separator_data) + { + priv->row_separator_destroy (priv->row_separator_data); + priv->row_separator_data = NULL; + } + + gtk_tree_view_set_model (tree_view, NULL); + + g_clear_object (&priv->hadjustment); + g_clear_object (&priv->vadjustment); + g_clear_object (&priv->horizontal_grid_line_texture); + g_clear_object (&priv->vertical_grid_line_texture); + g_clear_object (&priv->horizontal_tree_line_texture); + g_clear_object (&priv->vertical_tree_line_texture); + + G_OBJECT_CLASS (gtk_tree_view_parent_class)->dispose (object); +} + +/* GtkWidget::map helper */ +static void +gtk_tree_view_map_buttons (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GList *list; + + g_return_if_fail (gtk_widget_get_mapped (GTK_WIDGET (tree_view))); + + if (priv->headers_visible) + { + GtkTreeViewColumn *column; + GtkWidget *button; + + for (list = priv->columns; list; list = list->next) + { + column = list->data; + button = gtk_tree_view_column_get_button (column); + + if (gtk_tree_view_column_get_visible (column) && button) + gtk_widget_show (button); + + if (gtk_widget_get_visible (button) && + !gtk_widget_get_mapped (button)) + gtk_widget_map (button); + } + } +} + +static void +gtk_tree_view_map (GtkWidget *widget) +{ + GtkTreeView *tree_view = GTK_TREE_VIEW (widget); + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GList *tmp_list; + + GTK_WIDGET_CLASS (gtk_tree_view_parent_class)->map (widget); + + tmp_list = priv->children; + while (tmp_list) + { + GtkTreeViewChild *child = tmp_list->data; + tmp_list = tmp_list->next; + + if (gtk_widget_get_visible (child->widget)) + { + if (!gtk_widget_get_mapped (child->widget)) + gtk_widget_map (child->widget); + } + } + + gtk_tree_view_map_buttons (tree_view); +} + +static void +gtk_tree_view_realize (GtkWidget *widget) +{ + GtkTreeView *tree_view = GTK_TREE_VIEW (widget); + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GList *tmp_list; + + GTK_WIDGET_CLASS (gtk_tree_view_parent_class)->realize (widget); + + for (tmp_list = priv->columns; tmp_list; tmp_list = tmp_list->next) + _gtk_tree_view_column_realize_button (GTK_TREE_VIEW_COLUMN (tmp_list->data)); + + /* Need to call those here, since they create GCs */ + gtk_tree_view_set_grid_lines (tree_view, priv->grid_lines); + gtk_tree_view_set_enable_tree_lines (tree_view, priv->tree_lines_enabled); + + install_presize_handler (tree_view); +} + +static void +gtk_tree_view_unrealize (GtkWidget *widget) +{ + GtkTreeView *tree_view = GTK_TREE_VIEW (widget); + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + if (priv->scroll_timeout != 0) + { + g_source_remove (priv->scroll_timeout); + priv->scroll_timeout = 0; + } + + if (priv->auto_expand_timeout != 0) + { + g_source_remove (priv->auto_expand_timeout); + priv->auto_expand_timeout = 0; + } + + if (priv->open_dest_timeout != 0) + { + g_source_remove (priv->open_dest_timeout); + priv->open_dest_timeout = 0; + } + + if (priv->presize_handler_tick_cb != 0) + { + gtk_widget_remove_tick_callback (widget, priv->presize_handler_tick_cb); + priv->presize_handler_tick_cb = 0; + } + + if (priv->validate_rows_timer != 0) + { + g_source_remove (priv->validate_rows_timer); + priv->validate_rows_timer = 0; + } + + if (priv->scroll_sync_timer != 0) + { + g_source_remove (priv->scroll_sync_timer); + priv->scroll_sync_timer = 0; + } + + if (priv->typeselect_flush_timeout) + { + g_source_remove (priv->typeselect_flush_timeout); + priv->typeselect_flush_timeout = 0; + } + + GTK_WIDGET_CLASS (gtk_tree_view_parent_class)->unrealize (widget); +} + +static void +gtk_tree_view_unroot (GtkWidget *widget) +{ + GtkTreeView *tree_view = GTK_TREE_VIEW (widget); + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + /* break ref cycles */ + g_clear_pointer (&priv->scroll_to_path, gtk_tree_row_reference_free); + g_clear_pointer (&priv->drag_dest_row, gtk_tree_row_reference_free); + g_clear_pointer (&priv->top_row, gtk_tree_row_reference_free); + + GTK_WIDGET_CLASS (gtk_tree_view_parent_class)->unroot (widget); +} + +/* GtkWidget::get_preferred_height helper */ +static void +gtk_tree_view_update_height (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GList *list; + + priv->header_height = 0; + + for (list = priv->columns; list; list = list->next) + { + GtkRequisition requisition; + GtkTreeViewColumn *column = list->data; + GtkWidget *button = gtk_tree_view_column_get_button (column); + + if (button == NULL) + continue; + + gtk_widget_get_preferred_size (button, &requisition, NULL); + priv->header_height = MAX (priv->header_height, requisition.height); + } +} + +static int +gtk_tree_view_get_height (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + if (priv->tree == NULL) + return 0; + else + return priv->tree->root->offset; +} + +static void +gtk_tree_view_measure (GtkWidget *widget, + GtkOrientation orientation, + int for_size, + int *minimum, + int *natural, + int *minimum_baseline, + int *natural_baseline) +{ + GtkTreeView *tree_view = GTK_TREE_VIEW (widget); + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + if (orientation == GTK_ORIENTATION_HORIZONTAL) + { + GList *list; + GtkTreeViewColumn *column; + int width = 0; + + /* we validate some rows initially just to make sure we have some size. + * In practice, with a lot of static lists, this should get a good width. + */ + do_validate_rows (tree_view, FALSE); + + /* keep this in sync with size_allocate below */ + for (list = priv->columns; list; list = list->next) + { + column = list->data; + if (!gtk_tree_view_column_get_visible (column) || column == priv->drag_column) + continue; + + width += _gtk_tree_view_column_request_width (column); + } + + *minimum = *natural = width; + } + else /* VERTICAL */ + { + int height; + + gtk_tree_view_update_height (tree_view); + height = gtk_tree_view_get_height (tree_view) + gtk_tree_view_get_effective_header_height (tree_view); + + *minimum = *natural = height; + } +} + +static int +gtk_tree_view_calculate_width_before_expander (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + int width = 0; + GList *list; + gboolean rtl; + + rtl = (_gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); + for (list = (rtl ? g_list_last (priv->columns) : g_list_first (priv->columns)); + list->data != priv->expander_column; + list = (rtl ? list->prev : list->next)) + { + GtkTreeViewColumn *column = list->data; + + width += gtk_tree_view_column_get_width (column); + } + + return width; +} + +/* GtkWidget::size_allocate helper */ +static void +gtk_tree_view_size_allocate_columns (GtkWidget *widget) +{ + GtkTreeView *tree_view = GTK_TREE_VIEW (widget); + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + const int x_offset = - gtk_adjustment_get_value (priv->hadjustment); + GList *list, *first_column, *last_column; + GtkTreeViewColumn *column; + int widget_width, width = 0; + int extra, extra_per_column; + int full_requested_width = 0; + int number_of_expand_columns = 0; + gboolean rtl; + + for (last_column = g_list_last (priv->columns); + last_column && + !(gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (last_column->data))); + last_column = last_column->prev) + ; + if (last_column == NULL) + return; + + for (first_column = g_list_first (priv->columns); + first_column && + !(gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (first_column->data))); + first_column = first_column->next) + ; + + if (first_column == NULL) + return; + + rtl = (_gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL); + + /* find out how many extra space and expandable columns we have */ + for (list = priv->columns; list != last_column->next; list = list->next) + { + column = (GtkTreeViewColumn *)list->data; + + if (!gtk_tree_view_column_get_visible (column) || column == priv->drag_column) + continue; + + full_requested_width += _gtk_tree_view_column_request_width (column); + + if (gtk_tree_view_column_get_expand (column)) + number_of_expand_columns++; + } + + widget_width = gtk_widget_get_width (widget); + extra = MAX (widget_width - full_requested_width, 0); + + if (number_of_expand_columns > 0) + extra_per_column = extra/number_of_expand_columns; + else + extra_per_column = 0; + + for (list = first_column; + list != last_column->next; + list = list->next) + { + int column_width; + + column = list->data; + column_width = _gtk_tree_view_column_request_width (column); + + if (!gtk_tree_view_column_get_visible (column)) + continue; + + if (column == priv->drag_column) + goto next; + + if (gtk_tree_view_column_get_expand (column)) + { + if (number_of_expand_columns == 1) + { + /* We add the remander to the last column as + * */ + column_width += extra; + } + else + { + column_width += extra_per_column; + extra -= extra_per_column; + number_of_expand_columns --; + } + } + else if (number_of_expand_columns == 0 && + list == last_column) + { + column_width += extra; + } + + if (rtl) + _gtk_tree_view_column_allocate (column, widget_width - width - column_width + x_offset, + column_width, priv->header_height); + else + _gtk_tree_view_column_allocate (column, width + x_offset, + column_width, priv->header_height); + next: + width += column_width; + } + + /* We change the width here. The user might have been resizing columns, + * which changes the total width of the tree view. This is of + * importance for getting the horizontal scroll bar right. + */ + priv->width = width; +} + +/* GtkWidget::size_allocate helper */ +static void +gtk_tree_view_size_allocate_drag_column (GtkWidget *widget) +{ + GtkTreeView *tree_view = GTK_TREE_VIEW (widget); + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkAllocation allocation; + int baseline; + GtkWidget *button; + + if (priv->drag_column == NULL) + return; + + button = gtk_tree_view_column_get_button (priv->drag_column); + + allocation.x = priv->drag_column_x; + allocation.y = priv->drag_column_y; + allocation.width = gtk_widget_get_allocated_width (button); + allocation.height = gtk_widget_get_allocated_height (button); + baseline = gtk_widget_get_allocated_baseline (button); + + gtk_widget_size_allocate (button, &allocation, baseline); +} + +static void +gtk_tree_view_size_allocate (GtkWidget *widget, + int width, + int height, + int baseline) +{ + GtkTreeView *tree_view = GTK_TREE_VIEW (widget); + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GList *tmp_list; + double page_size; + + /* We allocate the columns first because the width of the + * tree view (used in updating the adjustments below) might change. + */ + gtk_tree_view_size_allocate_columns (widget); + gtk_tree_view_size_allocate_drag_column (widget); + + page_size = gtk_adjustment_get_page_size (priv->vadjustment); + gtk_adjustment_configure (priv->hadjustment, + gtk_adjustment_get_value (priv->hadjustment) + + (_gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL ? width - page_size : 0), + 0, + MAX (width, priv->width), + width * 0.1, + width * 0.9, + width); + + page_size = height - gtk_tree_view_get_effective_header_height (tree_view); + gtk_adjustment_configure (priv->vadjustment, + gtk_adjustment_get_value (priv->vadjustment), + 0, + MAX (page_size, gtk_tree_view_get_height (tree_view)), + page_size * 0.1, + page_size * 0.9, + page_size); + + /* now the adjustments and window sizes are in sync, we can sync toprow/dy again */ + if (gtk_tree_row_reference_valid (priv->top_row)) + gtk_tree_view_top_row_to_dy (tree_view); + else + gtk_tree_view_dy_to_top_row (tree_view); + + if (gtk_widget_get_realized (widget)) + { + if (priv->tree == NULL) + invalidate_empty_focus (tree_view); + + if (priv->expander_column) + { + /* Might seem awkward, but is the best heuristic I could come up + * with. Only if the width of the columns before the expander + * changes, we will update the prelight status. It is this + * width that makes the expander move vertically. Always updating + * prelight status causes trouble with hover selections. + */ + int width_before_expander; + + width_before_expander = gtk_tree_view_calculate_width_before_expander (tree_view); + + if (priv->prev_width_before_expander + != width_before_expander) + update_prelight (tree_view, + priv->event_last_x, + priv->event_last_y); + + priv->prev_width_before_expander = width_before_expander; + } + } + + for (tmp_list = priv->children; tmp_list; tmp_list = tmp_list->next) + { + GtkTreeViewChild *child = tmp_list->data; + GtkTreePath *path; + GdkRectangle child_rect; + int min_x, max_x, min_y, max_y; + int size; + GtkTextDirection direction; + + direction = _gtk_widget_get_direction (child->widget); + path = _gtk_tree_path_new_from_rbtree (child->tree, child->node); + gtk_tree_view_get_cell_area (tree_view, path, child->column, &child_rect); + child_rect.x += child->border.left; + child_rect.y += child->border.top; + child_rect.width -= child->border.left + child->border.right; + child_rect.height -= child->border.top + child->border.bottom; + + gtk_widget_measure (GTK_WIDGET (child->widget), GTK_ORIENTATION_HORIZONTAL, -1, + &size, NULL, NULL, NULL); + + if (size > child_rect.width) + { + /* Enlarge the child, extending it to the left (RTL) */ + if (direction == GTK_TEXT_DIR_RTL) + child_rect.x -= (size - child_rect.width); + /* or to the right (LTR) */ + else + child_rect.x += 0; + + child_rect.width = size; + } + + gtk_widget_measure (GTK_WIDGET (child->widget), GTK_ORIENTATION_VERTICAL, + child_rect.width, + &size, NULL, + NULL, NULL); + if (size > child_rect.height) + { + /* Enlarge the child, extending in both directions equally */ + child_rect.y -= (size - child_rect.height) / 2; + child_rect.height = size; + } + + /* push the rect back in the visible area if needed, + * preferring the top left corner (for RTL) + * or top right corner (for LTR) + */ + min_x = 0; + max_x = min_x + width - child_rect.width; + min_y = 0; + max_y = min_y + height - gtk_tree_view_get_effective_header_height (tree_view) - child_rect.height; + + if (direction == GTK_TEXT_DIR_LTR) + /* Ensure that child's right edge is not sticking to the right + * (if (child_rect.x > max_x) child_rect.x = max_x), + * then ensure that child's left edge is visible and is not sticking to the left + * (if (child_rect.x < min_x) child_rect.x = min_x). + */ + child_rect.x = MAX (min_x, MIN (max_x, child_rect.x)); + else + /* Ensure that child's left edge is not sticking to the left + * (if (child_rect.x < min_x) child_rect.x = min_x), + * then ensure that child's right edge is visible and is not sticking to the right + * (if (child_rect.x > max_x) child_rect.x = max_x). + */ + child_rect.x = MIN (max_x, MAX (min_x, child_rect.x)); + + child_rect.y = MAX (min_y, MIN (max_y, child_rect.y)); + + gtk_tree_path_free (path); + gtk_widget_size_allocate (child->widget, &child_rect, -1); + } + + if (priv->search_popover) + gtk_popover_present (GTK_POPOVER (priv->search_popover)); +} + +/* Grabs the focus and unsets the GTK_TREE_VIEW_DRAW_KEYFOCUS flag */ +static void +grab_focus_and_unset_draw_keyfocus (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkWidget *widget = GTK_WIDGET (tree_view); + + if (gtk_widget_get_focusable (widget) && + !gtk_widget_has_focus (widget)) + gtk_widget_grab_focus (widget); + + priv->draw_keyfocus = 0; +} + +static inline gboolean +row_is_separator (GtkTreeView *tree_view, + GtkTreeIter *iter, + GtkTreePath *path) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + gboolean is_separator = FALSE; + + if (priv->row_separator_func) + { + GtkTreeIter tmpiter; + + if (iter) + tmpiter = *iter; + else + { + if (!gtk_tree_model_get_iter (priv->model, &tmpiter, path)) + return FALSE; + } + + is_separator = priv->row_separator_func (priv->model, + &tmpiter, + priv->row_separator_data); + } + + return is_separator; +} + +static int +gtk_tree_view_get_expander_size (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkStyleContext *context; + GtkCssStyle *style; + int min_width; + int min_height; + int expander_size; + + if (priv->expander_size != -1) + return priv->expander_size; + + context = gtk_widget_get_style_context (GTK_WIDGET (tree_view)); + gtk_style_context_save (context); + gtk_style_context_add_class (context, "expander"); + + style = gtk_style_context_lookup_style (context); + min_width = _gtk_css_number_value_get (style->size->min_width, 100); + min_height = _gtk_css_number_value_get (style->size->min_height, 100); + + gtk_style_context_restore (context); + + expander_size = MAX (min_width, min_height); + + priv->expander_size = expander_size + (_TREE_VIEW_HORIZONTAL_SEPARATOR / 2); + + return priv->expander_size; +} + +static void +get_current_selection_modifiers (GtkEventController *controller, + gboolean *modify, + gboolean *extend) +{ + GdkModifierType state; + + state = gtk_event_controller_get_current_event_state (controller); + *modify = (state & GDK_CONTROL_MASK) != 0; + *extend = (state & GDK_SHIFT_MASK) != 0; +} + +static void +gtk_tree_view_click_gesture_pressed (GtkGestureClick *gesture, + int n_press, + double x, + double y, + GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkWidget *widget = GTK_WIDGET (tree_view); + GdkRectangle background_area, cell_area; + GtkTreeViewColumn *column = NULL; + GdkEventSequence *sequence; + GdkModifierType modifiers; + GdkEvent *event; + int new_y, y_offset; + int bin_x, bin_y; + GtkTreePath *path; + GtkTreeRBNode *node; + GtkTreeRBTree *tree; + int depth; + guint button; + GList *list; + gboolean rtl; + GtkWidget *target; + + gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, x, y, + &bin_x, &bin_y); + + /* Are we clicking a column header? */ + if (bin_y < 0) + return; + + /* check if this is a click in a child widget */ + target = gtk_event_controller_get_target (GTK_EVENT_CONTROLLER (gesture)); + if (gtk_widget_is_ancestor (target, widget)) + return; + + gtk_tree_view_stop_editing (tree_view, FALSE); + button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture)); + sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture)); + + if (button > 3) + { + gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED); + return; + } + + if (n_press > 1) + gtk_gesture_set_state (priv->drag_gesture, + GTK_EVENT_SEQUENCE_DENIED); + + /* Empty tree? */ + if (priv->tree == NULL) + { + grab_focus_and_unset_draw_keyfocus (tree_view); + return; + } + + if (sequence) + update_prelight (tree_view, x, y); + + /* are we in an arrow? */ + if (priv->prelight_node && + priv->arrow_prelit && + gtk_tree_view_draw_expanders (tree_view)) + { + if (button == GDK_BUTTON_PRIMARY) + { + priv->button_pressed_node = priv->prelight_node; + priv->button_pressed_tree = priv->prelight_tree; + gtk_widget_queue_draw (widget); + } + + grab_focus_and_unset_draw_keyfocus (tree_view); + gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED); + return; + } + + /* find the node that was clicked */ + new_y = TREE_WINDOW_Y_TO_RBTREE_Y(priv, bin_y); + if (new_y < 0) + new_y = 0; + y_offset = -gtk_tree_rbtree_find_offset (priv->tree, new_y, &tree, &node); + + if (node == NULL) + { + /* We clicked in dead space */ + grab_focus_and_unset_draw_keyfocus (tree_view); + return; + } + + /* Get the path and the node */ + path = _gtk_tree_path_new_from_rbtree (tree, node); + + if (row_is_separator (tree_view, NULL, path)) + { + gtk_tree_path_free (path); + grab_focus_and_unset_draw_keyfocus (tree_view); + return; + } + + depth = gtk_tree_path_get_depth (path); + background_area.y = y_offset + bin_y; + background_area.height = gtk_tree_view_get_row_height (tree_view, node); + background_area.x = 0; + + gtk_tree_view_convert_bin_window_to_widget_coords (tree_view, + background_area.x, + background_area.y, + &background_area.x, + &background_area.y); + + /* Let the column have a chance at selecting it. */ + rtl = (_gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL); + for (list = (rtl ? g_list_last (priv->columns) : g_list_first (priv->columns)); + list; list = (rtl ? list->prev : list->next)) + { + GtkTreeViewColumn *candidate = list->data; + + if (!gtk_tree_view_column_get_visible (candidate)) + continue; + + background_area.width = gtk_tree_view_column_get_width (candidate); + if ((background_area.x > x) || + (background_area.x + background_area.width <= x)) + { + background_area.x += background_area.width; + continue; + } + + /* we found the focus column */ + column = candidate; + cell_area = background_area; + cell_area.width -= _TREE_VIEW_HORIZONTAL_SEPARATOR; + cell_area.x += _TREE_VIEW_HORIZONTAL_SEPARATOR / 2; + if (gtk_tree_view_is_expander_column (tree_view, column)) + { + if (!rtl) + cell_area.x += (depth - 1) * priv->level_indentation; + cell_area.width -= (depth - 1) * priv->level_indentation; + + if (gtk_tree_view_draw_expanders (tree_view)) + { + int expander_size = gtk_tree_view_get_expander_size (tree_view); + if (!rtl) + cell_area.x += depth * expander_size; + cell_area.width -= depth * expander_size; + } + } + break; + } + + if (column == NULL) + { + gtk_tree_path_free (path); + grab_focus_and_unset_draw_keyfocus (tree_view); + gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED); + return; + } + + _gtk_tree_view_set_focus_column (tree_view, column); + + event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence); + modifiers = gdk_event_get_modifier_state (event); + + /* decide if we edit */ + if (button == GDK_BUTTON_PRIMARY && + !(modifiers & gtk_accelerator_get_default_mod_mask ())) + { + GtkTreePath *anchor; + GtkTreeIter iter; + + gtk_tree_model_get_iter (priv->model, &iter, path); + gtk_tree_view_column_cell_set_cell_data (column, + priv->model, + &iter, + GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_PARENT), + node->children?TRUE:FALSE); + + if (priv->anchor) + anchor = gtk_tree_row_reference_get_path (priv->anchor); + else + anchor = NULL; + + if ((anchor && !gtk_tree_path_compare (anchor, path)) + || !_gtk_tree_view_column_has_editable_cell (column)) + { + GtkCellEditable *cell_editable = NULL; + + /* FIXME: get the right flags */ + guint flags = 0; + + if (_gtk_tree_view_column_cell_event (column, + (GdkEvent *)event, + &cell_area, flags)) + { + GtkCellArea *area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (column)); + cell_editable = gtk_cell_area_get_edit_widget (area); + gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED); + + if (cell_editable != NULL) + { + gtk_tree_path_free (path); + gtk_tree_path_free (anchor); + return; + } + } + } + if (anchor) + gtk_tree_path_free (anchor); + } + + /* we only handle selection modifications on the first button press + */ + if (n_press == 1) + { + GtkCellRenderer *focus_cell; + gboolean modify, extend; + + get_current_selection_modifiers (GTK_EVENT_CONTROLLER (gesture), &modify, &extend); + priv->modify_selection_pressed = modify; + priv->extend_selection_pressed = extend; + + /* We update the focus cell here, this is also needed if the + * column does not contain an editable cell. In this case, + * GtkCellArea did not receive the event for processing (and + * could not update the focus cell). + */ + focus_cell = _gtk_tree_view_column_get_cell_at_pos (column, + &cell_area, + &background_area, + x, y); + + if (focus_cell) + gtk_tree_view_column_focus_cell (column, focus_cell); + + if (modify) + { + gtk_tree_view_real_set_cursor (tree_view, path, CLAMP_NODE); + gtk_tree_view_real_toggle_cursor_row (tree_view); + } + else if (extend) + { + gtk_tree_view_real_set_cursor (tree_view, path, CLAMP_NODE); + gtk_tree_view_real_select_cursor_row (tree_view, FALSE); + } + else + { + gtk_tree_view_real_set_cursor (tree_view, path, CLEAR_AND_SELECT | CLAMP_NODE); + } + + priv->modify_selection_pressed = FALSE; + priv->extend_selection_pressed = FALSE; + } + + if (button == GDK_BUTTON_PRIMARY && n_press == 2) + { + gtk_tree_view_row_activated (tree_view, path, column); + gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED); + } + else + { + if (n_press == 1) + { + priv->button_pressed_node = priv->prelight_node; + priv->button_pressed_tree = priv->prelight_tree; + } + + grab_focus_and_unset_draw_keyfocus (tree_view); + } + + gtk_tree_path_free (path); + + if (n_press >= 2) + gtk_event_controller_reset (GTK_EVENT_CONTROLLER (gesture)); +} + +static void +gtk_tree_view_drag_gesture_begin (GtkGestureDrag *gesture, + double start_x, + double start_y, + GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + int bin_x, bin_y; + GtkTreeRBTree *tree; + GtkTreeRBNode *node; + + if (priv->tree == NULL) + { + gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED); + return; + } + + gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, start_x, start_y, + &bin_x, &bin_y); + + /* Are we dragging a column header? */ + if (bin_y < 0) + return; + + priv->press_start_x = priv->rubber_band_x = bin_x; + priv->press_start_y = priv->rubber_band_y = bin_y; + gtk_tree_rbtree_find_offset (priv->tree, bin_y + priv->dy, + &tree, &node); + + if (priv->rubber_banding_enable + && !GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED) + && gtk_tree_selection_get_mode (priv->selection) == GTK_SELECTION_MULTIPLE) + { + gboolean modify, extend; + + priv->press_start_y += priv->dy; + priv->rubber_band_y += priv->dy; + priv->rubber_band_status = RUBBER_BAND_MAYBE_START; + + get_current_selection_modifiers (GTK_EVENT_CONTROLLER (gesture), &modify, &extend); + priv->rubber_band_modify = modify; + priv->rubber_band_extend = extend; + } +} + +static void +gtk_tree_view_column_click_gesture_pressed (GtkGestureClick *gesture, + int n_press, + double x, + double y, + GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreeViewColumn *column; + GList *list; + + if (n_press != 2) + return; + + for (list = priv->columns; list; list = list->next) + { + column = list->data; + + if (!_gtk_tree_view_column_coords_in_resize_rect (column, x, y) || + !gtk_tree_view_column_get_resizable (column)) + continue; + + if (gtk_tree_view_column_get_sizing (column) != GTK_TREE_VIEW_COLUMN_AUTOSIZE) + { + gtk_tree_view_column_set_fixed_width (column, -1); + gtk_tree_view_column_set_expand (column, FALSE); + _gtk_tree_view_column_autosize (tree_view, column); + } + + gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED); + break; + } +} + +static void +gtk_tree_view_column_drag_gesture_begin (GtkGestureDrag *gesture, + double start_x, + double start_y, + GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreeViewColumn *column; + gboolean rtl; + GList *list; + int i; + + rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); + + for (i = 0, list = priv->columns; list; list = list->next, i++) + { + gpointer drag_data; + int column_width; + + column = list->data; + + if (!_gtk_tree_view_column_coords_in_resize_rect (column, start_x, start_y)) + continue; + + if (!gtk_tree_view_column_get_resizable (column)) + break; + + priv->in_column_resize = TRUE; + + /* block attached dnd signal handler */ + drag_data = g_object_get_data (G_OBJECT (tree_view), "gtk-site-data"); + if (drag_data) + g_signal_handlers_block_matched (tree_view, + G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, + drag_data); + + column_width = gtk_tree_view_column_get_width (column); + gtk_tree_view_column_set_fixed_width (column, column_width); + gtk_tree_view_column_set_expand (column, FALSE); + + priv->drag_pos = i; + priv->x_drag = start_x + (rtl ? column_width : -column_width); + + if (!gtk_widget_has_focus (GTK_WIDGET (tree_view))) + gtk_widget_grab_focus (GTK_WIDGET (tree_view)); + + gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED); + return; + } +} + +static void +gtk_tree_view_update_button_position (GtkTreeView *tree_view, + GtkTreeViewColumn *column) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GList *column_el; + + column_el = g_list_find (priv->columns, column); + g_return_if_fail (column_el != NULL); + + gtk_css_node_insert_after (priv->header_node, + gtk_widget_get_css_node (gtk_tree_view_column_get_button (column)), + column_el->prev ? gtk_widget_get_css_node ( + gtk_tree_view_column_get_button (column_el->prev->data)) : NULL); +} + +/* column drag gesture helper */ +static gboolean +gtk_tree_view_button_release_drag_column (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkWidget *button, *widget = GTK_WIDGET (tree_view); + GList *l; + gboolean rtl; + GtkStyleContext *context; + + rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL); + + /* Move the button back */ + button = gtk_tree_view_column_get_button (priv->drag_column); + + context = gtk_widget_get_style_context (button); + gtk_style_context_remove_class (context, "dnd"); + + gtk_tree_view_update_button_position (tree_view, priv->drag_column); + gtk_widget_queue_allocate (widget); + + gtk_widget_grab_focus (button); + + if (rtl) + { + if (priv->cur_reorder && + priv->cur_reorder->right_column != priv->drag_column) + gtk_tree_view_move_column_after (tree_view, priv->drag_column, + priv->cur_reorder->right_column); + } + else + { + if (priv->cur_reorder && + priv->cur_reorder->left_column != priv->drag_column) + gtk_tree_view_move_column_after (tree_view, priv->drag_column, + priv->cur_reorder->left_column); + } + priv->drag_column = NULL; + + for (l = priv->column_drag_info; l != NULL; l = l->next) + g_slice_free (GtkTreeViewColumnReorder, l->data); + g_list_free (priv->column_drag_info); + priv->column_drag_info = NULL; + priv->cur_reorder = NULL; + + /* Reset our flags */ + priv->drag_column_surface_state = DRAG_COLUMN_WINDOW_STATE_UNSET; + priv->in_column_drag = FALSE; + + return TRUE; +} + +/* column drag gesture helper */ +static gboolean +gtk_tree_view_button_release_column_resize (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + gpointer drag_data; + + priv->drag_pos = -1; + + /* unblock attached dnd signal handler */ + drag_data = g_object_get_data (G_OBJECT (tree_view), "gtk-site-data"); + if (drag_data) + g_signal_handlers_unblock_matched (tree_view, + G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, + drag_data); + + priv->in_column_resize = FALSE; + return TRUE; +} + +static void +gtk_tree_view_column_drag_gesture_end (GtkGestureDrag *gesture, + double offset_x, + double offset_y, + GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GdkEventSequence *sequence; + + sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture)); + + /* Cancel reorder if the drag got cancelled */ + if (!gtk_gesture_handles_sequence (GTK_GESTURE (gesture), sequence)) + priv->cur_reorder = NULL; + + if (priv->in_column_drag) + gtk_tree_view_button_release_drag_column (tree_view); + else if (priv->in_column_resize) + gtk_tree_view_button_release_column_resize (tree_view); +} + +static void +gtk_tree_view_drag_gesture_end (GtkGestureDrag *gesture, + double offset_x, + double offset_y, + GtkTreeView *tree_view) +{ + gtk_tree_view_stop_rubber_band (tree_view); +} + +static void +gtk_tree_view_click_gesture_released (GtkGestureClick *gesture, + int n_press, + double x, + double y, + GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GdkEventSequence *sequence; + gboolean modify, extend; + guint button; + + button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture)); + sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture)); + + if (button != GDK_BUTTON_PRIMARY || + priv->button_pressed_node == NULL || + priv->button_pressed_node != priv->prelight_node) + return; + + get_current_selection_modifiers (GTK_EVENT_CONTROLLER (gesture), &modify, &extend); + + if (priv->arrow_prelit) + { + GtkTreePath *path = NULL; + + path = _gtk_tree_path_new_from_rbtree (priv->button_pressed_tree, + priv->button_pressed_node); + /* Actually activate the node */ + if (priv->button_pressed_node->children == NULL) + gtk_tree_view_real_expand_row (tree_view, path, + priv->button_pressed_tree, + priv->button_pressed_node, + FALSE); + else + gtk_tree_view_real_collapse_row (tree_view, path, + priv->button_pressed_tree, + priv->button_pressed_node); + gtk_tree_path_free (path); + } + else if (priv->activate_on_single_click && !modify && !extend) + { + GtkTreePath *path = NULL; + + path = _gtk_tree_path_new_from_rbtree (priv->button_pressed_tree, + priv->button_pressed_node); + gtk_tree_view_row_activated (tree_view, path, priv->focus_column); + gtk_tree_path_free (path); + } + + priv->button_pressed_tree = NULL; + priv->button_pressed_node = NULL; + + if (sequence) + ensure_unprelighted (tree_view); +} + +/* GtkWidget::motion_event function set. + */ + +static gboolean +coords_are_over_arrow (GtkTreeView *tree_view, + GtkTreeRBTree *tree, + GtkTreeRBNode *node, + /* these are in bin window coords */ + int x, + int y) +{ + GdkRectangle arrow; + int x2; + + if (!gtk_widget_get_realized (GTK_WIDGET (tree_view))) + return FALSE; + + if ((node->flags & GTK_TREE_RBNODE_IS_PARENT) == 0) + return FALSE; + + arrow.y = gtk_tree_view_get_row_y_offset (tree_view, tree, node); + arrow.height = gtk_tree_view_get_row_height (tree_view, node); + + gtk_tree_view_get_arrow_xrange (tree_view, tree, &arrow.x, &x2); + + arrow.width = x2 - arrow.x; + + return (x >= arrow.x && + x < (arrow.x + arrow.width) && + y >= arrow.y && + y < (arrow.y + arrow.height)); +} + +static gboolean +auto_expand_timeout (gpointer data) +{ + GtkTreeView *tree_view = GTK_TREE_VIEW (data); + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreePath *path; + + if (priv->prelight_node) + { + path = _gtk_tree_path_new_from_rbtree (priv->prelight_tree, + priv->prelight_node); + + if (priv->prelight_node->children) + gtk_tree_view_collapse_row (tree_view, path); + else + gtk_tree_view_expand_row (tree_view, path, FALSE); + + gtk_tree_path_free (path); + } + + priv->auto_expand_timeout = 0; + + return FALSE; +} + +static void +remove_auto_expand_timeout (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_clear_handle_id (&priv->auto_expand_timeout, g_source_remove); +} + +static void +do_prelight (GtkTreeView *tree_view, + GtkTreeRBTree *tree, + GtkTreeRBNode *node, + /* these are in bin_window coords */ + int x, + int y) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + if (priv->prelight_tree == tree && + priv->prelight_node == node) + { + /* We are still on the same node, + but we might need to take care of the arrow */ + + if (tree && node && gtk_tree_view_draw_expanders (tree_view)) + { + gboolean over_arrow; + + over_arrow = coords_are_over_arrow (tree_view, tree, node, x, y); + + if (over_arrow != priv->arrow_prelit) + { + if (over_arrow) + priv->arrow_prelit = TRUE; + else + priv->arrow_prelit = FALSE; + + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + } + } + + return; + } + + if (priv->prelight_tree && priv->prelight_node) + { + /* Unprelight the old node and arrow */ + + GTK_TREE_RBNODE_UNSET_FLAG (priv->prelight_node, + GTK_TREE_RBNODE_IS_PRELIT); + + if (priv->arrow_prelit + && gtk_tree_view_draw_expanders (tree_view)) + { + priv->arrow_prelit = FALSE; + + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + } + + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + } + + + if (priv->hover_expand) + remove_auto_expand_timeout (tree_view); + + /* Set the new prelight values */ + priv->prelight_node = node; + priv->prelight_tree = tree; + + if (!node || !tree) + return; + + /* Prelight the new node and arrow */ + + if (gtk_tree_view_draw_expanders (tree_view) + && coords_are_over_arrow (tree_view, tree, node, x, y)) + { + priv->arrow_prelit = TRUE; + + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + } + + GTK_TREE_RBNODE_SET_FLAG (node, GTK_TREE_RBNODE_IS_PRELIT); + + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + + if (priv->hover_expand) + { + priv->auto_expand_timeout = + g_timeout_add (AUTO_EXPAND_TIMEOUT, auto_expand_timeout, tree_view); + gdk_source_set_static_name_by_id (priv->auto_expand_timeout, "[gtk] auto_expand_timeout"); + } +} + +static void +prelight_or_select (GtkTreeView *tree_view, + GtkTreeRBTree *tree, + GtkTreeRBNode *node, + /* these are in bin_window coords */ + int x, + int y) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkSelectionMode mode = gtk_tree_selection_get_mode (priv->selection); + + if (priv->hover_selection && + (mode == GTK_SELECTION_SINGLE || mode == GTK_SELECTION_BROWSE) && + !(priv->edited_column && + gtk_cell_area_get_edit_widget + (gtk_cell_layout_get_area (GTK_CELL_LAYOUT (priv->edited_column))))) + { + if (node) + { + if (!GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED)) + { + GtkTreePath *path; + + path = _gtk_tree_path_new_from_rbtree (tree, node); + gtk_tree_selection_select_path (priv->selection, path); + if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED)) + { + priv->draw_keyfocus = FALSE; + gtk_tree_view_real_set_cursor (tree_view, path, 0); + } + gtk_tree_path_free (path); + } + } + + else if (mode == GTK_SELECTION_SINGLE) + gtk_tree_selection_unselect_all (priv->selection); + } + + do_prelight (tree_view, tree, node, x, y); +} + +static void +ensure_unprelighted (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv G_GNUC_UNUSED = gtk_tree_view_get_instance_private (tree_view); + + do_prelight (tree_view, + NULL, NULL, + -1000, -1000); /* coords not possibly over an arrow */ + + g_assert (priv->prelight_node == NULL); +} + +static void +update_prelight (GtkTreeView *tree_view, + int x, + int y) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + int new_y; + GtkTreeRBTree *tree; + GtkTreeRBNode *node; + + if (priv->tree == NULL) + return; + + if (x == -10000) + { + ensure_unprelighted (tree_view); + return; + } + + new_y = TREE_WINDOW_Y_TO_RBTREE_Y (priv, y); + if (new_y < 0) + new_y = 0; + + gtk_tree_rbtree_find_offset (priv->tree, + new_y, &tree, &node); + + if (node) + prelight_or_select (tree_view, tree, node, x, y); +} + +static gboolean +gtk_tree_view_motion_resize_column (GtkTreeView *tree_view, + double x, + double y) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + int new_width; + GtkTreeViewColumn *column; + + column = gtk_tree_view_get_column (tree_view, priv->drag_pos); + + if (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL) + new_width = MAX (priv->x_drag - x, 0); + else + new_width = MAX (x - priv->x_drag, 0); + + if (new_width != gtk_tree_view_column_get_fixed_width (column)) + gtk_tree_view_column_set_fixed_width (column, new_width); + + return FALSE; +} + +static void +gtk_tree_view_update_current_reorder (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreeViewColumnReorder *reorder = NULL; + GdkEventSequence *sequence; + GList *list; + double x; + + sequence = gtk_gesture_single_get_current_sequence + (GTK_GESTURE_SINGLE (priv->column_drag_gesture)); + gtk_gesture_get_point (priv->column_drag_gesture, + sequence, &x, NULL); + x += gtk_adjustment_get_value (priv->hadjustment); + + for (list = priv->column_drag_info; list; list = list->next) + { + reorder = (GtkTreeViewColumnReorder *) list->data; + if (x >= reorder->left_align && x < reorder->right_align) + break; + reorder = NULL; + } + + priv->cur_reorder = reorder; +} + +static void +gtk_tree_view_vertical_autoscroll (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GdkRectangle visible_rect; + int y; + int offset; + + if (gtk_gesture_is_recognized (priv->drag_gesture)) + { + GdkEventSequence *sequence; + double py; + + sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (priv->drag_gesture)); + gtk_gesture_get_point (priv->drag_gesture, sequence, NULL, &py); + gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, 0, py, + NULL, &y); + } + else + { + y = priv->event_last_y; + gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, 0, y, NULL, &y); + } + + y += priv->dy; + gtk_tree_view_get_visible_rect (tree_view, &visible_rect); + + /* see if we are near the edge. */ + offset = y - (visible_rect.y + 2 * SCROLL_EDGE_SIZE); + if (offset > 0) + { + offset = y - (visible_rect.y + visible_rect.height - 2 * SCROLL_EDGE_SIZE); + if (offset < 0) + return; + } + + gtk_adjustment_set_value (priv->vadjustment, + MAX (gtk_adjustment_get_value (priv->vadjustment) + offset, 0.0)); +} + +static void +gtk_tree_view_horizontal_autoscroll (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GdkEventSequence *sequence; + GdkRectangle visible_rect; + double x; + int offset; + + sequence = gtk_gesture_single_get_current_sequence + (GTK_GESTURE_SINGLE (priv->column_drag_gesture)); + gtk_gesture_get_point (priv->column_drag_gesture, + sequence, &x, NULL); + gtk_tree_view_get_visible_rect (tree_view, &visible_rect); + + x += gtk_adjustment_get_value (priv->hadjustment); + + /* See if we are near the edge. */ + offset = x - (visible_rect.x + SCROLL_EDGE_SIZE); + if (offset > 0) + { + offset = x - (visible_rect.x + visible_rect.width - SCROLL_EDGE_SIZE); + if (offset < 0) + return; + } + offset = offset/3; + + gtk_adjustment_set_value (priv->hadjustment, + MAX (gtk_adjustment_get_value (priv->hadjustment) + offset, 0.0)); +} + +static void +gtk_tree_view_motion_drag_column (GtkTreeView *tree_view, + double x, + double y) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreeViewColumn *column = priv->drag_column; + GtkWidget *button; + int width, button_width; + + button = gtk_tree_view_column_get_button (column); + x += gtk_adjustment_get_value (priv->hadjustment); + + /* Handle moving the header */ + width = gtk_widget_get_allocated_width (GTK_WIDGET (tree_view)); + button_width = gtk_widget_get_allocated_width (button); + priv->drag_column_x = CLAMP (x - _gtk_tree_view_column_get_drag_x (column), 0, + MAX (priv->width, width) - button_width); + + /* autoscroll, if needed */ + gtk_tree_view_horizontal_autoscroll (tree_view); + /* Update the current reorder position and arrow; */ + gtk_tree_view_update_current_reorder (tree_view); + gtk_widget_queue_allocate (GTK_WIDGET (tree_view)); +} + +static void +gtk_tree_view_stop_rubber_band (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + remove_scroll_timeout (tree_view); + + if (priv->rubber_band_status == RUBBER_BAND_ACTIVE) + { + GtkTreePath *tmp_path; + + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + + /* The anchor path should be set to the start path */ + if (priv->rubber_band_start_node) + { + tmp_path = _gtk_tree_path_new_from_rbtree (priv->rubber_band_start_tree, + priv->rubber_band_start_node); + + if (priv->anchor) + gtk_tree_row_reference_free (priv->anchor); + + priv->anchor = gtk_tree_row_reference_new (priv->model, tmp_path); + + gtk_tree_path_free (tmp_path); + } + + /* ... and the cursor to the end path */ + if (priv->rubber_band_end_node) + { + tmp_path = _gtk_tree_path_new_from_rbtree (priv->rubber_band_end_tree, + priv->rubber_band_end_node); + gtk_tree_view_real_set_cursor (GTK_TREE_VIEW (tree_view), tmp_path, 0); + gtk_tree_path_free (tmp_path); + } + + _gtk_tree_selection_emit_changed (priv->selection); + + gtk_css_node_set_parent (priv->rubber_band_cssnode, NULL); + priv->rubber_band_cssnode = NULL; + } + + /* Clear status variables */ + priv->rubber_band_status = RUBBER_BAND_OFF; + priv->rubber_band_extend = FALSE; + priv->rubber_band_modify = FALSE; + + priv->rubber_band_start_node = NULL; + priv->rubber_band_start_tree = NULL; + priv->rubber_band_end_node = NULL; + priv->rubber_band_end_tree = NULL; +} + +static void +gtk_tree_view_update_rubber_band_selection_range (GtkTreeView *tree_view, + GtkTreeRBTree *start_tree, + GtkTreeRBNode *start_node, + GtkTreeRBTree *end_tree, + GtkTreeRBNode *end_node, + gboolean select, + gboolean skip_start, + gboolean skip_end) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + if (start_node == end_node) + return; + + /* We skip the first node and jump inside the loop */ + if (skip_start) + goto skip_first; + + do + { + /* Small optimization by assuming insensitive nodes are never + * selected. + */ + if (!GTK_TREE_RBNODE_FLAG_SET (start_node, GTK_TREE_RBNODE_IS_SELECTED)) + { + GtkTreePath *path; + gboolean selectable; + + path = _gtk_tree_path_new_from_rbtree (start_tree, start_node); + selectable = _gtk_tree_selection_row_is_selectable (priv->selection, start_node, path); + gtk_tree_path_free (path); + + if (!selectable) + goto node_not_selectable; + } + + if (select) + { + if (priv->rubber_band_extend) + GTK_TREE_RBNODE_SET_FLAG (start_node, GTK_TREE_RBNODE_IS_SELECTED); + else if (priv->rubber_band_modify) + { + /* Toggle the selection state */ + if (GTK_TREE_RBNODE_FLAG_SET (start_node, GTK_TREE_RBNODE_IS_SELECTED)) + GTK_TREE_RBNODE_UNSET_FLAG (start_node, GTK_TREE_RBNODE_IS_SELECTED); + else + GTK_TREE_RBNODE_SET_FLAG (start_node, GTK_TREE_RBNODE_IS_SELECTED); + } + else + GTK_TREE_RBNODE_SET_FLAG (start_node, GTK_TREE_RBNODE_IS_SELECTED); + } + else + { + /* Mirror the above */ + if (priv->rubber_band_extend) + GTK_TREE_RBNODE_UNSET_FLAG (start_node, GTK_TREE_RBNODE_IS_SELECTED); + else if (priv->rubber_band_modify) + { + /* Toggle the selection state */ + if (GTK_TREE_RBNODE_FLAG_SET (start_node, GTK_TREE_RBNODE_IS_SELECTED)) + GTK_TREE_RBNODE_UNSET_FLAG (start_node, GTK_TREE_RBNODE_IS_SELECTED); + else + GTK_TREE_RBNODE_SET_FLAG (start_node, GTK_TREE_RBNODE_IS_SELECTED); + } + else + GTK_TREE_RBNODE_UNSET_FLAG (start_node, GTK_TREE_RBNODE_IS_SELECTED); + } + + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + +node_not_selectable: + if (start_node == end_node) + break; + +skip_first: + + if (start_node->children) + { + start_tree = start_node->children; + start_node = gtk_tree_rbtree_first (start_tree); + } + else + { + gtk_tree_rbtree_next_full (start_tree, start_node, &start_tree, &start_node); + + if (!start_tree) + /* Ran out of tree */ + break; + } + + if (skip_end && start_node == end_node) + break; + } + while (TRUE); +} + +static void +gtk_tree_view_update_rubber_band_selection (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreeRBTree *start_tree, *end_tree; + GtkTreeRBNode *start_node, *end_node; + double start_y, offset_y; + int bin_y; + + if (!gtk_gesture_is_active (priv->drag_gesture)) + return; + + gtk_gesture_drag_get_offset (GTK_GESTURE_DRAG (priv->drag_gesture), + NULL, &offset_y); + gtk_gesture_drag_get_start_point (GTK_GESTURE_DRAG (priv->drag_gesture), + NULL, &start_y); + gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, 0, start_y, + NULL, &bin_y); + bin_y = MAX (0, bin_y + offset_y + priv->dy); + + gtk_tree_rbtree_find_offset (priv->tree, MIN (priv->press_start_y, bin_y), &start_tree, &start_node); + gtk_tree_rbtree_find_offset (priv->tree, MAX (priv->press_start_y, bin_y), &end_tree, &end_node); + + /* Handle the start area first */ + if (!start_node && !end_node) + { + if (priv->rubber_band_start_node) + { + GtkTreeRBNode *node = priv->rubber_band_start_node; + + if (priv->rubber_band_modify) + { + /* Toggle the selection state */ + if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED)) + GTK_TREE_RBNODE_UNSET_FLAG (node, GTK_TREE_RBNODE_IS_SELECTED); + else + GTK_TREE_RBNODE_SET_FLAG (node, GTK_TREE_RBNODE_IS_SELECTED); + } + else + GTK_TREE_RBNODE_UNSET_FLAG (node, GTK_TREE_RBNODE_IS_SELECTED); + + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + } + } + if (!priv->rubber_band_start_node || !start_node) + { + gtk_tree_view_update_rubber_band_selection_range (tree_view, + start_tree, + start_node, + end_tree, + end_node, + TRUE, + FALSE, + FALSE); + } + else if (gtk_tree_rbtree_node_find_offset (start_tree, start_node) < + gtk_tree_rbtree_node_find_offset (priv->rubber_band_start_tree, priv->rubber_band_start_node)) + { + /* New node is above the old one; selection became bigger */ + gtk_tree_view_update_rubber_band_selection_range (tree_view, + start_tree, + start_node, + priv->rubber_band_start_tree, + priv->rubber_band_start_node, + TRUE, + FALSE, + TRUE); + } + else if (gtk_tree_rbtree_node_find_offset (start_tree, start_node) > + gtk_tree_rbtree_node_find_offset (priv->rubber_band_start_tree, priv->rubber_band_start_node)) + { + /* New node is below the old one; selection became smaller */ + gtk_tree_view_update_rubber_band_selection_range (tree_view, + priv->rubber_band_start_tree, + priv->rubber_band_start_node, + start_tree, + start_node, + FALSE, + FALSE, + TRUE); + } + + priv->rubber_band_start_tree = start_tree; + priv->rubber_band_start_node = start_node; + + /* Next, handle the end area */ + if (!priv->rubber_band_end_node) + { + /* In the event this happens, start_node was also NULL; this case is + * handled above. + */ + } + else if (!end_node) + { + /* Find the last node in the tree */ + gtk_tree_rbtree_find_offset (priv->tree, gtk_tree_view_get_height (tree_view) - 1, + &end_tree, &end_node); + + /* Selection reached end of the tree */ + gtk_tree_view_update_rubber_band_selection_range (tree_view, + priv->rubber_band_end_tree, + priv->rubber_band_end_node, + end_tree, + end_node, + TRUE, + TRUE, + FALSE); + } + else if (gtk_tree_rbtree_node_find_offset (end_tree, end_node) > + gtk_tree_rbtree_node_find_offset (priv->rubber_band_end_tree, priv->rubber_band_end_node)) + { + /* New node is below the old one; selection became bigger */ + gtk_tree_view_update_rubber_band_selection_range (tree_view, + priv->rubber_band_end_tree, + priv->rubber_band_end_node, + end_tree, + end_node, + TRUE, + TRUE, + FALSE); + } + else if (gtk_tree_rbtree_node_find_offset (end_tree, end_node) < + gtk_tree_rbtree_node_find_offset (priv->rubber_band_end_tree, priv->rubber_band_end_node)) + { + /* New node is above the old one; selection became smaller */ + gtk_tree_view_update_rubber_band_selection_range (tree_view, + end_tree, + end_node, + priv->rubber_band_end_tree, + priv->rubber_band_end_node, + FALSE, + TRUE, + FALSE); + } + + priv->rubber_band_end_tree = end_tree; + priv->rubber_band_end_node = end_node; +} + +static void +gtk_tree_view_update_rubber_band (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + double start_x, start_y, offset_x, offset_y, x, y; + int bin_x, bin_y; + + if (!gtk_gesture_is_recognized (priv->drag_gesture)) + return; + + gtk_gesture_drag_get_offset (GTK_GESTURE_DRAG (priv->drag_gesture), + &offset_x, &offset_y); + gtk_gesture_drag_get_start_point (GTK_GESTURE_DRAG (priv->drag_gesture), + &start_x, &start_y); + gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, start_x, start_y, + &bin_x, &bin_y); + bin_y += priv->dy; + + x = MAX (bin_x + offset_x, 0); + y = MAX (bin_y + offset_y, 0); + + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + + priv->rubber_band_x = x; + priv->rubber_band_y = y; + + gtk_tree_view_update_rubber_band_selection (tree_view); +} + +static void +gtk_tree_view_snapshot_rubber_band (GtkTreeView *tree_view, + GtkSnapshot *snapshot) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + double start_x, start_y, offset_x, offset_y; + GdkRectangle rect; + GtkStyleContext *context; + int bin_x, bin_y; + + if (!gtk_gesture_is_recognized (priv->drag_gesture)) + return; + + gtk_gesture_drag_get_offset (GTK_GESTURE_DRAG (priv->drag_gesture), + &offset_x, &offset_y); + gtk_gesture_drag_get_start_point (GTK_GESTURE_DRAG (priv->drag_gesture), + &start_x, &start_y); + gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, start_x, start_y, + &bin_x, &bin_y); + bin_x = MAX (0, bin_x + offset_x); + bin_y = MAX (0, bin_y + offset_y + priv->dy); + + context = gtk_widget_get_style_context (GTK_WIDGET (tree_view)); + + gtk_style_context_save_to_node (context, priv->rubber_band_cssnode); + + rect.x = MIN (priv->press_start_x, bin_x); + rect.y = MIN (priv->press_start_y, bin_y) - priv->dy; + rect.width = ABS (priv->press_start_x - bin_x) + 1; + rect.height = ABS (priv->press_start_y - bin_y) + 1; + + gtk_snapshot_render_background (snapshot, context, + rect.x, rect.y, + rect.width, rect.height); + gtk_snapshot_render_frame (snapshot, context, + rect.x, rect.y, + rect.width, rect.height); + + gtk_style_context_restore (context); +} + +static void +gtk_tree_view_column_drag_gesture_update (GtkGestureDrag *gesture, + double offset_x, + double offset_y, + GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + double start_x, start_y, x, y; + GdkEventSequence *sequence; + + sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture)); + + if (gtk_gesture_get_sequence_state (GTK_GESTURE (gesture), sequence) != GTK_EVENT_SEQUENCE_CLAIMED) + return; + + gtk_gesture_drag_get_start_point (gesture, &start_x, &start_y); + x = start_x + offset_x; + y = start_y + offset_y; + + if (priv->in_column_resize) + gtk_tree_view_motion_resize_column (tree_view, x, y); + else if (priv->in_column_drag) + gtk_tree_view_motion_drag_column (tree_view, x, y); +} + +static void +gtk_tree_view_drag_gesture_update (GtkGestureDrag *gesture, + double offset_x, + double offset_y, + GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + if (priv->tree == NULL) + { + gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED); + return; + } + + if (priv->rubber_band_status == RUBBER_BAND_MAYBE_START) + { + GtkCssNode *widget_node; + + widget_node = gtk_widget_get_css_node (GTK_WIDGET (tree_view)); + priv->rubber_band_cssnode = gtk_css_node_new (); + gtk_css_node_set_name (priv->rubber_band_cssnode, g_quark_from_static_string ("rubberband")); + gtk_css_node_set_parent (priv->rubber_band_cssnode, widget_node); + gtk_css_node_set_state (priv->rubber_band_cssnode, gtk_css_node_get_state (widget_node)); + g_object_unref (priv->rubber_band_cssnode); + + gtk_tree_view_update_rubber_band (tree_view); + + priv->rubber_band_status = RUBBER_BAND_ACTIVE; + gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED); + } + else if (priv->rubber_band_status == RUBBER_BAND_ACTIVE) + { + gtk_tree_view_update_rubber_band (tree_view); + + add_scroll_timeout (tree_view); + } + else if (!priv->rubber_band_status) + { + if (gtk_tree_view_maybe_begin_dragging_row (tree_view)) + gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED); + } +} + +static void +gtk_tree_view_motion_controller_motion (GtkEventControllerMotion *controller, + double x, + double y, + GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreeRBTree *tree; + GtkTreeRBNode *node; + int new_y; + GList *list; + gboolean cursor_set = FALSE; + + if (priv->tree) + { + int bin_x, bin_y; + + /* If we are currently pressing down a button, we don't want to prelight anything else. */ + if (gtk_gesture_is_active (priv->drag_gesture) || + gtk_gesture_is_active (priv->click_gesture)) + node = NULL; + + gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, x, y, + &bin_x, &bin_y); + new_y = MAX (0, TREE_WINDOW_Y_TO_RBTREE_Y (priv, bin_y)); + + gtk_tree_rbtree_find_offset (priv->tree, new_y, &tree, &node); + + priv->event_last_x = bin_x; + priv->event_last_y = bin_y; + prelight_or_select (tree_view, tree, node, bin_x, bin_y); + } + + for (list = priv->columns; list; list = list->next) + { + GtkTreeViewColumn *column = list->data; + + if (_gtk_tree_view_column_coords_in_resize_rect (column, x, y)) + { + gtk_widget_set_cursor_from_name (GTK_WIDGET (tree_view), "col-resize"); + cursor_set = TRUE; + break; + } + } + + if (!cursor_set) + gtk_widget_set_cursor (GTK_WIDGET (tree_view), NULL); +} + +/* Invalidate the focus rectangle near the edge of the bin_window; used when + * the tree is empty. + */ +static void +invalidate_empty_focus (GtkTreeView *tree_view) +{ + if (!gtk_widget_has_focus (GTK_WIDGET (tree_view))) + return; + + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); +} + +static void +gtk_tree_view_snapshot_grid_line (GtkTreeView *tree_view, + GtkSnapshot *snapshot, + GtkOrientation orientation, + const graphene_point_t *start, + float size) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkStyleContext *context; + const GdkRGBA *grid_line_color; + + context = gtk_widget_get_style_context (GTK_WIDGET (tree_view)); + grid_line_color = gtk_css_color_value_get_rgba (_gtk_style_context_peek_property (context, + GTK_CSS_PROPERTY_BORDER_TOP_COLOR)); + + if (!gdk_rgba_equal (grid_line_color, &priv->grid_line_color) || + (orientation == GTK_ORIENTATION_HORIZONTAL && !priv->horizontal_grid_line_texture) || + (orientation == GTK_ORIENTATION_VERTICAL && !priv->vertical_grid_line_texture)) + { + cairo_surface_t *surface; + unsigned char *data; + + g_clear_object (&priv->horizontal_grid_line_texture); + g_clear_object (&priv->vertical_grid_line_texture); + priv->grid_line_color = *grid_line_color; + + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 2, 1); + data = cairo_image_surface_get_data (surface); + /* just color the first pixel... */ + data[0] = round (grid_line_color->blue * 255); + data[1] = round (grid_line_color->green * 255); + data[2] = round (grid_line_color->red * 255); + data[3] = round (grid_line_color->alpha * 255); + + priv->horizontal_grid_line_texture = gdk_texture_new_for_surface (surface); + cairo_surface_destroy (surface); + + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 2); + data = cairo_image_surface_get_data (surface); + data[0] = round (grid_line_color->blue * 255); + data[1] = round (grid_line_color->green * 255); + data[2] = round (grid_line_color->red * 255); + data[3] = round (grid_line_color->alpha * 255); + + priv->vertical_grid_line_texture = gdk_texture_new_for_surface (surface); + cairo_surface_destroy (surface); + } + + g_assert (priv->horizontal_grid_line_texture); + g_assert (priv->vertical_grid_line_texture); + + if (orientation == GTK_ORIENTATION_HORIZONTAL) + { + gtk_snapshot_push_repeat (snapshot, + &GRAPHENE_RECT_INIT ( + start->x, start->y, + size, 1), + NULL); + gtk_snapshot_append_texture (snapshot, priv->horizontal_grid_line_texture, + &GRAPHENE_RECT_INIT (0, 0, 2, 1)); + gtk_snapshot_pop (snapshot); + } + else /* VERTICAL */ + { + gtk_snapshot_push_repeat (snapshot, + &GRAPHENE_RECT_INIT ( + start->x, start->y, + 1, size), + NULL); + gtk_snapshot_append_texture (snapshot, priv->vertical_grid_line_texture, + &GRAPHENE_RECT_INIT (0, 0, 1, 2)); + gtk_snapshot_pop (snapshot); + } +} + +static void +gtk_tree_view_snapshot_tree_line (GtkTreeView *tree_view, + GtkSnapshot *snapshot, + GtkOrientation orientation, + const graphene_point_t *start, + float size) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkStyleContext *context; + const GdkRGBA *tree_line_color; + + context = gtk_widget_get_style_context (GTK_WIDGET (tree_view)); + tree_line_color = gtk_css_color_value_get_rgba (_gtk_style_context_peek_property (context, + GTK_CSS_PROPERTY_BORDER_LEFT_COLOR)); + + if (!gdk_rgba_equal (tree_line_color, &priv->tree_line_color) || + (orientation == GTK_ORIENTATION_HORIZONTAL && !priv->horizontal_tree_line_texture) || + (orientation == GTK_ORIENTATION_VERTICAL && !priv->vertical_tree_line_texture)) + { + cairo_surface_t *surface; + unsigned char *data; + + g_clear_object (&priv->horizontal_tree_line_texture); + g_clear_object (&priv->vertical_tree_line_texture); + priv->tree_line_color = *tree_line_color; + + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 2, 1); + data = cairo_image_surface_get_data (surface); + /* just color the first pixel... */ + data[0] = round (tree_line_color->blue * 255); + data[1] = round (tree_line_color->green * 255); + data[2] = round (tree_line_color->red * 255); + data[3] = round (tree_line_color->alpha * 255); + + priv->horizontal_tree_line_texture = gdk_texture_new_for_surface (surface); + cairo_surface_destroy (surface); + + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 2); + data = cairo_image_surface_get_data (surface); + data[0] = round (tree_line_color->blue * 255); + data[1] = round (tree_line_color->green * 255); + data[2] = round (tree_line_color->red * 255); + data[3] = round (tree_line_color->alpha * 255); + + priv->vertical_tree_line_texture = gdk_texture_new_for_surface (surface); + cairo_surface_destroy (surface); + } + + g_assert (priv->horizontal_tree_line_texture); + g_assert (priv->vertical_tree_line_texture); + + if (orientation == GTK_ORIENTATION_HORIZONTAL) + { + gtk_snapshot_push_repeat (snapshot, + &GRAPHENE_RECT_INIT ( + start->x, start->y, + size, 1), + NULL); + gtk_snapshot_append_texture (snapshot, priv->horizontal_tree_line_texture, + &GRAPHENE_RECT_INIT (0, 0, 2, 1)); + gtk_snapshot_pop (snapshot); + } + else /* VERTICAL */ + { + gtk_snapshot_push_repeat (snapshot, + &GRAPHENE_RECT_INIT ( + start->x, start->y, + 1, size), + NULL); + gtk_snapshot_append_texture (snapshot, priv->vertical_tree_line_texture, + &GRAPHENE_RECT_INIT (0, 0, 1, 2)); + gtk_snapshot_pop (snapshot); + } +} + +static void +gtk_tree_view_snapshot_grid_lines (GtkTreeView *tree_view, + GtkSnapshot *snapshot) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GList *list, *first, *last; + gboolean rtl; + int current_x = 0; + int tree_view_height; + + if (priv->grid_lines != GTK_TREE_VIEW_GRID_LINES_VERTICAL && + priv->grid_lines != GTK_TREE_VIEW_GRID_LINES_BOTH) + return; + + rtl = (_gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); + + first = g_list_first (priv->columns); + last = g_list_last (priv->columns); + tree_view_height = gtk_tree_view_get_height (tree_view); + + for (list = (rtl ? last : first); + list; + list = (rtl ? list->prev : list->next)) + { + GtkTreeViewColumn *column = list->data; + + /* We don't want a line for the last column */ + if (column == (rtl ? first->data : last->data)) + break; + + if (!gtk_tree_view_column_get_visible (column)) + continue; + + current_x += gtk_tree_view_column_get_width (column); + + gtk_tree_view_snapshot_grid_line (tree_view, + snapshot, + GTK_ORIENTATION_VERTICAL, + &(graphene_point_t) { current_x - 1, 0 }, + tree_view_height); + } +} + +/* Warning: Very scary function. + * Modify at your own risk + * + * KEEP IN SYNC WITH gtk_tree_view_create_row_drag_icon()! + * FIXME: It’s not... + */ +static void +gtk_tree_view_bin_snapshot (GtkWidget *widget, + GtkSnapshot *snapshot) +{ + GtkTreeView *tree_view = GTK_TREE_VIEW (widget); + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + const int x_scroll_offset = - gtk_adjustment_get_value (priv->hadjustment); + GtkTreePath *path; + GtkTreeRBTree *tree; + GList *list; + GtkTreeRBNode *node; + GtkTreeRBNode *drag_highlight = NULL; + GtkTreeRBTree *drag_highlight_tree = NULL; + GtkTreeIter iter; + int new_y; + int y_offset, cell_offset; + int max_height; + int depth; + GdkRectangle background_area; + GdkRectangle cell_area; + GdkRectangle clip; + guint flags; + int bin_window_width; + int bin_window_height; + GtkTreePath *drag_dest_path; + GList *first_column, *last_column; + gboolean has_can_focus_cell; + gboolean rtl; + int n_visible_columns; + int expander_size; + gboolean draw_vgrid_lines, draw_hgrid_lines; + GtkStyleContext *context; + gboolean parity; + + rtl = (_gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL); + context = gtk_widget_get_style_context (widget); + + if (priv->tree == NULL) + return; + + bin_window_width = gtk_widget_get_width (GTK_WIDGET (tree_view)); + bin_window_height = gtk_widget_get_height(GTK_WIDGET (tree_view)); + + clip = (GdkRectangle) { 0, 0, bin_window_width, bin_window_height }; + new_y = TREE_WINDOW_Y_TO_RBTREE_Y (priv, clip.y); + y_offset = -gtk_tree_rbtree_find_offset (priv->tree, new_y, &tree, &node); + + if (gtk_tree_view_get_height (tree_view) < bin_window_height) + { + gtk_style_context_save (context); + gtk_style_context_add_class (context, "cell"); + + gtk_snapshot_render_background (snapshot, context, + 0, gtk_tree_view_get_height (tree_view), + bin_window_width, + bin_window_height - gtk_tree_view_get_height (tree_view)); + + gtk_style_context_restore (context); + } + + if (node == NULL) + return; + + /* find the path for the node */ + path = _gtk_tree_path_new_from_rbtree (tree, node); + gtk_tree_model_get_iter (priv->model, + &iter, + path); + depth = gtk_tree_path_get_depth (path); + gtk_tree_path_free (path); + + drag_dest_path = NULL; + + if (priv->drag_dest_row) + drag_dest_path = gtk_tree_row_reference_get_path (priv->drag_dest_row); + + if (drag_dest_path) + _gtk_tree_view_find_node (tree_view, drag_dest_path, + &drag_highlight_tree, &drag_highlight); + + draw_vgrid_lines = + priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_VERTICAL + || priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_BOTH; + draw_hgrid_lines = + priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_HORIZONTAL + || priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_BOTH; + expander_size = gtk_tree_view_get_expander_size (tree_view); + + n_visible_columns = 0; + for (list = priv->columns; list; list = list->next) + { + if (!gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (list->data))) + continue; + n_visible_columns ++; + } + + /* Find the last column */ + for (last_column = g_list_last (priv->columns); + last_column && + !(gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (last_column->data))); + last_column = last_column->prev) + ; + + /* and the first */ + for (first_column = g_list_first (priv->columns); + first_column && + !(gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (first_column->data))); + first_column = first_column->next) + ; + + /* Actually process the expose event. To do this, we want to + * start at the first node of the event, and walk the tree in + * order, drawing each successive node. + */ + + parity = !(gtk_tree_rbtree_node_get_index (tree, node) % 2); + + do + { + gboolean is_separator = FALSE; + int n_col = 0; + + parity = !parity; + is_separator = row_is_separator (tree_view, &iter, NULL); + + max_height = gtk_tree_view_get_row_height (tree_view, node); + + cell_offset = x_scroll_offset; + + background_area.y = y_offset + clip.y; + background_area.height = max_height; + + flags = 0; + + if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_PRELIT)) + flags |= GTK_CELL_RENDERER_PRELIT; + + if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED)) + flags |= GTK_CELL_RENDERER_SELECTED; + + /* we *need* to set cell data on all cells before the call + * to _has_can_focus_cell, else _has_can_focus_cell() does not + * return a correct value. + */ + for (list = (rtl ? g_list_last (priv->columns) : g_list_first (priv->columns)); + list; + list = (rtl ? list->prev : list->next)) + { + GtkTreeViewColumn *column = list->data; + gtk_tree_view_column_cell_set_cell_data (column, + priv->model, + &iter, + GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_PARENT), + node->children?TRUE:FALSE); + } + + has_can_focus_cell = gtk_tree_view_has_can_focus_cell (tree_view); + + for (list = (rtl ? g_list_last (priv->columns) : g_list_first (priv->columns)); + list; + list = (rtl ? list->prev : list->next)) + { + GtkTreeViewColumn *column = list->data; + GtkStateFlags state = 0; + int width; + gboolean draw_focus; + + if (!gtk_tree_view_column_get_visible (column)) + continue; + + n_col++; + width = gtk_tree_view_column_get_width (column); + + if (cell_offset > clip.x + clip.width || + cell_offset + width < clip.x) + { + cell_offset += width; + continue; + } + + if (gtk_tree_view_column_get_sort_indicator (column)) + flags |= GTK_CELL_RENDERER_SORTED; + else + flags &= ~GTK_CELL_RENDERER_SORTED; + + if (priv->cursor_node == node) + flags |= GTK_CELL_RENDERER_FOCUSED; + else + flags &= ~GTK_CELL_RENDERER_FOCUSED; + + if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_PARENT)) + flags |= GTK_CELL_RENDERER_EXPANDABLE; + else + flags &= ~GTK_CELL_RENDERER_EXPANDABLE; + + if (node->children) + flags |= GTK_CELL_RENDERER_EXPANDED; + else + flags &= ~GTK_CELL_RENDERER_EXPANDED; + + background_area.x = cell_offset; + background_area.width = width; + + cell_area = background_area; + cell_area.x += _TREE_VIEW_HORIZONTAL_SEPARATOR /2; + cell_area.width -= _TREE_VIEW_HORIZONTAL_SEPARATOR; + + if (draw_vgrid_lines) + { + if (list == first_column) + { + cell_area.width -= _TREE_VIEW_GRID_LINE_WIDTH / 2; + } + else if (list == last_column) + { + cell_area.x += _TREE_VIEW_GRID_LINE_WIDTH / 2; + cell_area.width -= _TREE_VIEW_GRID_LINE_WIDTH / 2; + } + else + { + cell_area.x += _TREE_VIEW_GRID_LINE_WIDTH / 2; + cell_area.width -= _TREE_VIEW_GRID_LINE_WIDTH; + } + } + + if (draw_hgrid_lines) + { + cell_area.y += _TREE_VIEW_GRID_LINE_WIDTH / 2; + cell_area.height -= _TREE_VIEW_GRID_LINE_WIDTH; + } + + if (!gdk_rectangle_intersect (&clip, &background_area, NULL)) + { + cell_offset += gtk_tree_view_column_get_width (column); + continue; + } + + background_area.x -= x_scroll_offset; + cell_area.x -= x_scroll_offset; + + gtk_tree_view_column_cell_set_cell_data (column, + priv->model, + &iter, + GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_PARENT), + node->children?TRUE:FALSE); + + gtk_style_context_save (context); + + state = gtk_cell_renderer_get_state (NULL, widget, flags); + gtk_style_context_set_state (context, state); + + gtk_style_context_add_class (context, "cell"); + + if (node == priv->cursor_node && has_can_focus_cell + && ((column == priv->focus_column + && priv->draw_keyfocus && + gtk_widget_has_visible_focus (widget)) + || (column == priv->edited_column))) + draw_focus = TRUE; + else + draw_focus = FALSE; + + /* Draw background */ + gtk_snapshot_render_background (snapshot, context, + background_area.x, + background_area.y, + background_area.width, + background_area.height); + + /* Draw frame */ + gtk_snapshot_render_frame (snapshot, context, + background_area.x, + background_area.y, + background_area.width, + background_area.height); + + if (gtk_tree_view_is_expander_column (tree_view, column)) + { + if (!rtl) + cell_area.x += (depth - 1) * priv->level_indentation; + cell_area.width -= (depth - 1) * priv->level_indentation; + + if (gtk_tree_view_draw_expanders (tree_view)) + { + if (!rtl) + cell_area.x += depth * expander_size; + cell_area.width -= depth * expander_size; + } + + if (is_separator) + { + GdkRGBA color; + + gtk_style_context_save (context); + gtk_style_context_add_class (context, "separator"); + + gtk_style_context_get_color (context, &color); + gtk_snapshot_append_color (snapshot, + &color, + &GRAPHENE_RECT_INIT( + cell_area.x, + cell_area.y + cell_area.height / 2, + cell_area.x + cell_area.width, + 1 + )); + + gtk_style_context_restore (context); + } + else + { + gtk_tree_view_column_cell_snapshot (column, + snapshot, + &background_area, + &cell_area, + flags, + draw_focus); + } + + if (gtk_tree_view_draw_expanders (tree_view) + && (node->flags & GTK_TREE_RBNODE_IS_PARENT) == GTK_TREE_RBNODE_IS_PARENT) + { + gtk_tree_view_snapshot_arrow (GTK_TREE_VIEW (widget), + snapshot, + tree, + node); + } + } + else + { + if (is_separator) + { + GdkRGBA color; + + gtk_style_context_save (context); + gtk_style_context_add_class (context, "separator"); + + gtk_style_context_get_color (context, &color); + gtk_snapshot_append_color (snapshot, + &color, + &GRAPHENE_RECT_INIT( + cell_area.x, + cell_area.y + cell_area.height / 2, + cell_area.x + cell_area.width, + 1 + )); + + gtk_style_context_restore (context); + } + else + gtk_tree_view_column_cell_snapshot (column, + snapshot, + &background_area, + &cell_area, + flags, + draw_focus); + } + + if (draw_hgrid_lines) + { + if (background_area.y >= clip.y) + gtk_tree_view_snapshot_grid_line (tree_view, + snapshot, + GTK_ORIENTATION_HORIZONTAL, + &(graphene_point_t) { + background_area.x, background_area.y + }, + background_area.width); + + if (background_area.y + max_height < clip.y + clip.height) + gtk_tree_view_snapshot_grid_line (tree_view, + snapshot, + GTK_ORIENTATION_HORIZONTAL, + &(graphene_point_t) { + background_area.x, background_area.y + max_height + }, + background_area.width); + } + + if (gtk_tree_view_is_expander_column (tree_view, column) && + priv->tree_lines_enabled) + { + int x = background_area.x; + int mult = rtl ? -1 : 1; + int y0 = background_area.y; + int y1 = background_area.y + background_area.height/2; + int y2 = background_area.y + background_area.height; + + if (rtl) + x += background_area.width - 1; + + if ((node->flags & GTK_TREE_RBNODE_IS_PARENT) == GTK_TREE_RBNODE_IS_PARENT && + depth > 1) + { + gtk_tree_view_snapshot_tree_line (tree_view, + snapshot, + GTK_ORIENTATION_HORIZONTAL, + &(graphene_point_t) { + x + expander_size * (depth - 1.5) * mult, + y1 + }, + mult * expander_size * 0.4); + + } + else if (depth > 1) + { + gtk_tree_view_snapshot_tree_line (tree_view, + snapshot, + GTK_ORIENTATION_HORIZONTAL, + &(graphene_point_t) { + x + expander_size * (depth - 1.5) * mult, + y1 + }, + mult * expander_size); + } + + if (depth > 1) + { + int i; + GtkTreeRBNode *tmp_node; + GtkTreeRBTree *tmp_tree; + + if (!gtk_tree_rbtree_next (tree, node)) + gtk_tree_view_snapshot_tree_line (tree_view, + snapshot, + GTK_ORIENTATION_VERTICAL, + &(graphene_point_t) { + x + expander_size * (depth - 1.5) * mult, + y0 + }, + y1 - y0); + else + gtk_tree_view_snapshot_tree_line (tree_view, + snapshot, + GTK_ORIENTATION_VERTICAL, + &(graphene_point_t) { + x + expander_size * (depth - 1.5) * mult, + y0 + }, + y2 - y0); + + tmp_node = tree->parent_node; + tmp_tree = tree->parent_tree; + + for (i = depth - 2; i > 0; i--) + { + if (gtk_tree_rbtree_next (tmp_tree, tmp_node)) + gtk_tree_view_snapshot_tree_line (tree_view, + snapshot, + GTK_ORIENTATION_VERTICAL, + &(graphene_point_t) { + x + expander_size * (i - 0.5) * mult, + y0 + }, + y2 - y0); + tmp_node = tmp_tree->parent_node; + tmp_tree = tmp_tree->parent_tree; + } + } + } + + gtk_style_context_restore (context); + cell_offset += gtk_tree_view_column_get_width (column); + } + + if (node == drag_highlight) + { + GtkTreeRBTree *drag_tree = NULL; + GtkTreeRBNode *drag_node = NULL; + + _gtk_tree_view_find_node (tree_view, drag_dest_path, &drag_tree, &drag_node); + if (drag_tree != NULL) + { + TreeViewDragInfo *di; + + di = get_info (tree_view); + /* Draw indicator for the drop + */ + + switch (priv->drag_dest_pos) + { + case GTK_TREE_VIEW_DROP_BEFORE: + gtk_css_node_set_classes (di->cssnode, (const char *[]){"before", NULL}); + break; + + case GTK_TREE_VIEW_DROP_AFTER: + gtk_css_node_set_classes (di->cssnode, (const char *[]){"after", NULL}); + break; + + case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: + case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: + gtk_css_node_set_classes (di->cssnode, (const char *[]){"into", NULL}); + break; + + default: + break; + } + + gtk_style_context_save_to_node (context, di->cssnode); + gtk_style_context_set_state (context, gtk_style_context_get_state (context) | GTK_STATE_FLAG_DROP_ACTIVE); + + gtk_snapshot_render_frame (snapshot, context, + 0, gtk_tree_view_get_row_y_offset (tree_view, drag_tree, drag_node), + bin_window_width, + gtk_tree_view_get_row_height (tree_view, drag_node)); + + gtk_style_context_restore (context); + } + } + + /* draw the big row-spanning focus rectangle, if needed */ + if (!has_can_focus_cell && node == priv->cursor_node && + priv->draw_keyfocus && + gtk_widget_has_visible_focus (widget)) + { + int tmp_y, tmp_height; + GtkStateFlags focus_rect_state = 0; + + gtk_style_context_save (context); + + focus_rect_state = gtk_cell_renderer_get_state (NULL, widget, flags); + gtk_style_context_set_state (context, focus_rect_state); + + if (draw_hgrid_lines) + { + tmp_y = gtk_tree_view_get_row_y_offset (tree_view, tree, node) + _TREE_VIEW_GRID_LINE_WIDTH / 2; + tmp_height = gtk_tree_view_get_row_height (tree_view, node) - _TREE_VIEW_GRID_LINE_WIDTH; + } + else + { + tmp_y = gtk_tree_view_get_row_y_offset (tree_view, tree, node); + tmp_height = gtk_tree_view_get_row_height (tree_view, node); + } + + gtk_snapshot_render_focus (snapshot, context, + 0, tmp_y, + bin_window_width, + tmp_height); + + gtk_style_context_restore (context); + } + + y_offset += max_height; + if (node->children) + { + GtkTreeIter parent = iter; + gboolean has_child; + + tree = node->children; + node = gtk_tree_rbtree_first (tree); + + has_child = gtk_tree_model_iter_children (priv->model, + &iter, + &parent); + depth++; + + /* Sanity Check! */ + TREE_VIEW_INTERNAL_ASSERT_VOID (has_child); + } + else + { + gboolean done = FALSE; + + do + { + node = gtk_tree_rbtree_next (tree, node); + if (node != NULL) + { + gboolean has_next = gtk_tree_model_iter_next (priv->model, &iter); + done = TRUE; + + /* Sanity Check! */ + TREE_VIEW_INTERNAL_ASSERT_VOID (has_next); + } + else + { + GtkTreeIter parent_iter = iter; + gboolean has_parent; + + node = tree->parent_node; + tree = tree->parent_tree; + if (tree == NULL) + /* we should go to done to free some memory */ + goto done; + has_parent = gtk_tree_model_iter_parent (priv->model, + &iter, + &parent_iter); + depth--; + + /* Sanity check */ + TREE_VIEW_INTERNAL_ASSERT_VOID (has_parent); + } + } + while (!done); + } + } + while (y_offset < clip.height); + +done: + gtk_tree_view_snapshot_grid_lines (tree_view, snapshot); + + if (priv->rubber_band_status == RUBBER_BAND_ACTIVE) + gtk_tree_view_snapshot_rubber_band (tree_view, snapshot); + + if (drag_dest_path) + gtk_tree_path_free (drag_dest_path); +} + +static void +gtk_tree_view_snapshot (GtkWidget *widget, + GtkSnapshot *snapshot) +{ + GtkTreeView *tree_view = GTK_TREE_VIEW (widget); + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkWidget *button; + GtkStyleContext *context; + GList *list; + int width, height; + + context = gtk_widget_get_style_context (widget); + width = gtk_widget_get_width (widget); + height = gtk_widget_get_height (widget); + + gtk_snapshot_push_clip (snapshot, + &GRAPHENE_RECT_INIT( + 0, gtk_tree_view_get_effective_header_height (tree_view), + width, + height - gtk_tree_view_get_effective_header_height (tree_view) + )); + + gtk_snapshot_save (snapshot); + gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT ( + - (int) gtk_adjustment_get_value (priv->hadjustment), + gtk_tree_view_get_effective_header_height (tree_view))); + gtk_tree_view_bin_snapshot (widget, snapshot); + gtk_snapshot_restore (snapshot); + + /* We can't just chain up to Container::draw as it will try to send the + * event to the headers, so we handle propagating it to our children + * (eg. widgets being edited) ourselves. + */ + for (list = priv->children; list; list = list->next) + { + GtkTreeViewChild *child = list->data; + + gtk_widget_snapshot_child (widget, child->widget, snapshot); + } + + gtk_snapshot_pop (snapshot); + + gtk_snapshot_push_clip (snapshot, + &GRAPHENE_RECT_INIT( + 0, 0, + width, + gtk_tree_view_get_effective_header_height (tree_view) + )); + + gtk_style_context_save (context); + gtk_style_context_remove_class (context, "view"); + + for (list = priv->columns; list != NULL; list = list->next) + { + GtkTreeViewColumn *column = list->data; + + if (column == priv->drag_column) + continue; + + if (gtk_tree_view_column_get_visible (column)) + { + button = gtk_tree_view_column_get_button (column); + gtk_widget_snapshot_child (widget, button, snapshot); + } + } + + if (priv->drag_column) + { + button = gtk_tree_view_column_get_button (priv->drag_column); + gtk_widget_snapshot_child (widget, button, snapshot); + } + + gtk_style_context_restore (context); + + gtk_snapshot_pop (snapshot); +} + +enum +{ + DROP_HOME, + DROP_RIGHT, + DROP_LEFT, + DROP_END +}; + +/* returns 0x1 when no column has been found -- yes it's hackish */ +static GtkTreeViewColumn * +gtk_tree_view_get_drop_column (GtkTreeView *tree_view, + GtkTreeViewColumn *column, + int drop_position) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreeViewColumn *left_column = NULL; + GtkTreeViewColumn *cur_column = NULL; + GList *tmp_list; + + if (!gtk_tree_view_column_get_reorderable (column)) + return (GtkTreeViewColumn *)0x1; + + switch (drop_position) + { + case DROP_HOME: + /* find first column where we can drop */ + tmp_list = priv->columns; + if (column == GTK_TREE_VIEW_COLUMN (tmp_list->data)) + return (GtkTreeViewColumn *)0x1; + + while (tmp_list) + { + g_assert (tmp_list); + + cur_column = GTK_TREE_VIEW_COLUMN (tmp_list->data); + tmp_list = tmp_list->next; + + if (left_column && + gtk_tree_view_column_get_visible (left_column) == FALSE) + continue; + + if (!priv->column_drop_func) + return left_column; + + if (!priv->column_drop_func (tree_view, column, left_column, cur_column, priv->column_drop_func_data)) + { + left_column = cur_column; + continue; + } + + return left_column; + } + + if (!priv->column_drop_func) + return left_column; + + if (priv->column_drop_func (tree_view, column, left_column, NULL, priv->column_drop_func_data)) + return left_column; + else + return (GtkTreeViewColumn *)0x1; + break; + + case DROP_RIGHT: + /* find first column after column where we can drop */ + tmp_list = priv->columns; + + for (; tmp_list; tmp_list = tmp_list->next) + if (GTK_TREE_VIEW_COLUMN (tmp_list->data) == column) + break; + + if (!tmp_list || !tmp_list->next) + return (GtkTreeViewColumn *)0x1; + + tmp_list = tmp_list->next; + left_column = GTK_TREE_VIEW_COLUMN (tmp_list->data); + tmp_list = tmp_list->next; + + while (tmp_list) + { + g_assert (tmp_list); + + cur_column = GTK_TREE_VIEW_COLUMN (tmp_list->data); + tmp_list = tmp_list->next; + + if (left_column && + gtk_tree_view_column_get_visible (left_column) == FALSE) + { + left_column = cur_column; + if (tmp_list) + tmp_list = tmp_list->next; + continue; + } + + if (!priv->column_drop_func) + return left_column; + + if (!priv->column_drop_func (tree_view, column, left_column, cur_column, priv->column_drop_func_data)) + { + left_column = cur_column; + continue; + } + + return left_column; + } + + if (!priv->column_drop_func) + return left_column; + + if (priv->column_drop_func (tree_view, column, left_column, NULL, priv->column_drop_func_data)) + return left_column; + else + return (GtkTreeViewColumn *)0x1; + break; + + case DROP_LEFT: + /* find first column before column where we can drop */ + tmp_list = priv->columns; + + for (; tmp_list; tmp_list = tmp_list->next) + if (GTK_TREE_VIEW_COLUMN (tmp_list->data) == column) + break; + + if (!tmp_list || !tmp_list->prev) + return (GtkTreeViewColumn *)0x1; + + tmp_list = tmp_list->prev; + cur_column = GTK_TREE_VIEW_COLUMN (tmp_list->data); + tmp_list = tmp_list->prev; + + while (tmp_list) + { + g_assert (tmp_list); + + left_column = GTK_TREE_VIEW_COLUMN (tmp_list->data); + + if (left_column && + gtk_tree_view_column_get_visible (left_column) == FALSE) + { + /*if (!tmp_list->prev) + return (GtkTreeViewColumn *)0x1; + */ +/* + cur_column = GTK_TREE_VIEW_COLUMN (tmp_list->prev->data); + tmp_list = tmp_list->prev->prev; + continue;*/ + + cur_column = left_column; + if (tmp_list) + tmp_list = tmp_list->prev; + continue; + } + + if (!priv->column_drop_func) + return left_column; + + if (priv->column_drop_func (tree_view, column, left_column, cur_column, priv->column_drop_func_data)) + return left_column; + + cur_column = left_column; + tmp_list = tmp_list->prev; + } + + if (!priv->column_drop_func) + return NULL; + + if (priv->column_drop_func (tree_view, column, NULL, cur_column, priv->column_drop_func_data)) + return NULL; + else + return (GtkTreeViewColumn *)0x1; + break; + + case DROP_END: + /* same as DROP_HOME case, but doing it backwards */ + tmp_list = g_list_last (priv->columns); + cur_column = NULL; + + if (column == GTK_TREE_VIEW_COLUMN (tmp_list->data)) + return (GtkTreeViewColumn *)0x1; + + while (tmp_list) + { + g_assert (tmp_list); + + left_column = GTK_TREE_VIEW_COLUMN (tmp_list->data); + + if (left_column && + gtk_tree_view_column_get_visible (left_column) == FALSE) + { + cur_column = left_column; + tmp_list = tmp_list->prev; + } + + if (!priv->column_drop_func) + return left_column; + + if (priv->column_drop_func (tree_view, column, left_column, cur_column, priv->column_drop_func_data)) + return left_column; + + cur_column = left_column; + tmp_list = tmp_list->prev; + } + + if (!priv->column_drop_func) + return NULL; + + if (priv->column_drop_func (tree_view, column, NULL, cur_column, priv->column_drop_func_data)) + return NULL; + else + return (GtkTreeViewColumn *)0x1; + break; + + default: + return (GtkTreeViewColumn *)0x1; + break; + } +} + +static gboolean +gtk_tree_view_search_key_cancels_search (guint keyval) +{ + return keyval == GDK_KEY_Escape + || keyval == GDK_KEY_Tab + || keyval == GDK_KEY_KP_Tab + || keyval == GDK_KEY_ISO_Left_Tab; +} + +static gboolean +gtk_tree_view_key_controller_key_pressed (GtkEventControllerKey *key, + guint keyval, + guint keycode, + GdkModifierType state, + GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkWidget *widget = GTK_WIDGET (tree_view); + GtkWidget *button; + + if (priv->rubber_band_status) + { + if (keyval == GDK_KEY_Escape) + gtk_tree_view_stop_rubber_band (tree_view); + + return TRUE; + } + + if (priv->in_column_drag) + { + if (keyval == GDK_KEY_Escape) + gtk_gesture_set_state (GTK_GESTURE (priv->column_drag_gesture), + GTK_EVENT_SEQUENCE_DENIED); + return TRUE; + } + + if (priv->headers_visible) + { + GList *focus_column; + gboolean rtl; + + rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); + + for (focus_column = priv->columns; + focus_column; + focus_column = focus_column->next) + { + GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN (focus_column->data); + + button = gtk_tree_view_column_get_button (column); + if (gtk_widget_has_focus (button)) + break; + } + + if (focus_column && + (state & GDK_SHIFT_MASK) && (state & GDK_ALT_MASK) && + (keyval == GDK_KEY_Left || keyval == GDK_KEY_KP_Left + || keyval == GDK_KEY_Right || keyval == GDK_KEY_KP_Right)) + { + GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN (focus_column->data); + int column_width; + + if (!gtk_tree_view_column_get_resizable (column)) + { + gtk_widget_error_bell (widget); + return TRUE; + } + + column_width = gtk_tree_view_column_get_width (column); + + if (keyval == (rtl ? GDK_KEY_Right : GDK_KEY_Left) + || keyval == (rtl ? GDK_KEY_KP_Right : GDK_KEY_KP_Left)) + { + column_width = MAX (column_width - 2, 0); + } + else if (keyval == (rtl ? GDK_KEY_Left : GDK_KEY_Right) + || keyval == (rtl ? GDK_KEY_KP_Left : GDK_KEY_KP_Right)) + { + column_width = column_width + 2; + } + + gtk_tree_view_column_set_fixed_width (column, column_width); + gtk_tree_view_column_set_expand (column, FALSE); + return TRUE; + } + + if (focus_column && + (state & GDK_ALT_MASK) && + (keyval == GDK_KEY_Left || keyval == GDK_KEY_KP_Left + || keyval == GDK_KEY_Right || keyval == GDK_KEY_KP_Right + || keyval == GDK_KEY_Home || keyval == GDK_KEY_KP_Home + || keyval == GDK_KEY_End || keyval == GDK_KEY_KP_End)) + { + GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN (focus_column->data); + + if (keyval == (rtl ? GDK_KEY_Right : GDK_KEY_Left) + || keyval == (rtl ? GDK_KEY_KP_Right : GDK_KEY_KP_Left)) + { + GtkTreeViewColumn *col; + col = gtk_tree_view_get_drop_column (tree_view, column, DROP_LEFT); + if (col != (GtkTreeViewColumn *)0x1) + gtk_tree_view_move_column_after (tree_view, column, col); + else + gtk_widget_error_bell (widget); + } + else if (keyval == (rtl ? GDK_KEY_Left : GDK_KEY_Right) + || keyval == (rtl ? GDK_KEY_KP_Left : GDK_KEY_KP_Right)) + { + GtkTreeViewColumn *col; + col = gtk_tree_view_get_drop_column (tree_view, column, DROP_RIGHT); + if (col != (GtkTreeViewColumn *)0x1) + gtk_tree_view_move_column_after (tree_view, column, col); + else + gtk_widget_error_bell (widget); + } + else if (keyval == GDK_KEY_Home || keyval == GDK_KEY_KP_Home) + { + GtkTreeViewColumn *col; + col = gtk_tree_view_get_drop_column (tree_view, column, DROP_HOME); + if (col != (GtkTreeViewColumn *)0x1) + gtk_tree_view_move_column_after (tree_view, column, col); + else + gtk_widget_error_bell (widget); + } + else if (keyval == GDK_KEY_End || keyval == GDK_KEY_KP_End) + { + GtkTreeViewColumn *col; + col = gtk_tree_view_get_drop_column (tree_view, column, DROP_END); + if (col != (GtkTreeViewColumn *)0x1) + gtk_tree_view_move_column_after (tree_view, column, col); + else + gtk_widget_error_bell (widget); + } + + return TRUE; + } + } + + return FALSE; +} + +static gboolean +gtk_tree_view_forward_controller_key_pressed (GtkEventControllerKey *key, + guint keyval, + guint keycode, + GdkModifierType state, + GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + if (priv->search_entry_avoid_unhandled_binding) + { + priv->search_entry_avoid_unhandled_binding = FALSE; + return FALSE; + } + + /* Initially, before the search window is visible, we pass the event to the + * IM context of the search entry box. If it triggers a commit or a preedit, + * we then show the search window without losing tree view focus. + * If the search window is already visible, we forward the events to it, + * keeping the focus on the tree view. + */ + if (gtk_widget_has_focus (GTK_WIDGET (tree_view)) + && priv->enable_search + && !priv->search_custom_entry_set + && !gtk_tree_view_search_key_cancels_search (keyval)) + { + gtk_tree_view_ensure_interactive_directory (tree_view); + + if (!gtk_widget_is_visible (priv->search_popover)) + { + priv->imcontext_changed = FALSE; + + gtk_event_controller_key_forward (key, priv->search_entry); + + if (priv->imcontext_changed) + return gtk_tree_view_real_start_interactive_search (tree_view, FALSE); + } + } + + return FALSE; +} + +static void +gtk_tree_view_key_controller_key_released (GtkEventControllerKey *key, + guint keyval, + guint keycode, + GdkModifierType state, + GtkTreeView *tree_view) +{ +} + +static void +gtk_tree_view_motion_controller_enter (GtkEventControllerMotion *controller, + double x, + double y, + GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreeRBTree *tree; + GtkTreeRBNode *node; + int new_y; + + if (priv->tree == NULL) + return; + + /* find the node internally */ + new_y = TREE_WINDOW_Y_TO_RBTREE_Y(priv, y); + if (new_y < 0) + new_y = 0; + gtk_tree_rbtree_find_offset (priv->tree, new_y, &tree, &node); + + priv->event_last_x = x; + priv->event_last_y = y; + + if ((priv->button_pressed_node == NULL) || + (priv->button_pressed_node == node)) + prelight_or_select (tree_view, tree, node, x, y); +} + +static void +gtk_tree_view_motion_controller_leave (GtkEventControllerMotion *controller, + GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + if (priv->prelight_node) + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + + priv->event_last_x = -10000; + priv->event_last_y = -10000; + + prelight_or_select (tree_view, NULL, NULL, -1000, -1000); /* not possibly over an arrow */ +} + +static void +gtk_tree_view_focus_controller_focus_out (GtkEventController *focus, + GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + + if (priv->search_popover && + !gtk_event_controller_focus_contains_focus (GTK_EVENT_CONTROLLER_FOCUS (focus))) + gtk_tree_view_search_popover_hide (priv->search_popover, tree_view); +} + +/* Incremental Reflow + */ + +static int +get_separator_height (GtkTreeView *tree_view) +{ + GtkStyleContext *context; + GtkCssStyle *style; + double d; + int min_size; + + context = gtk_widget_get_style_context (GTK_WIDGET (tree_view)); + gtk_style_context_save (context); + gtk_style_context_add_class (context, "separator"); + + style = gtk_style_context_lookup_style (context); + d = _gtk_css_number_value_get (style->size->min_height, 100); + + if (d < 1) + min_size = ceil (d); + else + min_size = floor (d); + + gtk_style_context_restore (context); + + return min_size; +} + +/* Returns TRUE if it updated the size + */ +static gboolean +validate_row (GtkTreeView *tree_view, + GtkTreeRBTree *tree, + GtkTreeRBNode *node, + GtkTreeIter *iter, + GtkTreePath *path) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreeViewColumn *column; + GtkStyleContext *context; + GList *list, *first_column, *last_column; + int height = 0; + int depth = gtk_tree_path_get_depth (path); + gboolean retval = FALSE; + gboolean is_separator = FALSE; + gboolean draw_vgrid_lines, draw_hgrid_lines; + int expander_size; + int separator_height; + + /* double check the row needs validating */ + if (! GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_INVALID) && + ! GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_COLUMN_INVALID)) + return FALSE; + + is_separator = row_is_separator (tree_view, iter, NULL); + + draw_vgrid_lines = + priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_VERTICAL + || priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_BOTH; + draw_hgrid_lines = + priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_HORIZONTAL + || priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_BOTH; + expander_size = gtk_tree_view_get_expander_size (tree_view); + + for (last_column = g_list_last (priv->columns); + last_column && + !(gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (last_column->data))); + last_column = last_column->prev) + ; + + for (first_column = g_list_first (priv->columns); + first_column && + !(gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (first_column->data))); + first_column = first_column->next) + ; + + separator_height = get_separator_height (tree_view); + + context = gtk_widget_get_style_context (GTK_WIDGET (tree_view)); + gtk_style_context_save (context); + gtk_style_context_add_class (context, "cell"); + + for (list = priv->columns; list; list = list->next) + { + int padding = 0; + int original_width; + int new_width; + int row_height; + + column = list->data; + + if (!gtk_tree_view_column_get_visible (column)) + continue; + + if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_COLUMN_INVALID) && + !_gtk_tree_view_column_cell_get_dirty (column)) + continue; + + original_width = _gtk_tree_view_column_get_requested_width (column); + + gtk_tree_view_column_cell_set_cell_data (column, priv->model, iter, + GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_PARENT), + node->children?TRUE:FALSE); + gtk_tree_view_column_cell_get_size (column, + NULL, NULL, + NULL, &row_height); + + if (is_separator) + { + height = separator_height; + /* gtk_tree_view_get_row_height() assumes separator nodes are > 0 */ + height = MAX (height, 1); + } + else + { + height = MAX (height, row_height); + height = MAX (height, expander_size); + } + + if (gtk_tree_view_is_expander_column (tree_view, column)) + { + padding += _TREE_VIEW_HORIZONTAL_SEPARATOR + (depth - 1) * priv->level_indentation; + + if (gtk_tree_view_draw_expanders (tree_view)) + padding += depth * expander_size; + } + else + padding += _TREE_VIEW_HORIZONTAL_SEPARATOR; + + if (draw_vgrid_lines) + { + if (list->data == first_column || list->data == last_column) + padding += _TREE_VIEW_GRID_LINE_WIDTH / 2.0; + else + padding += _TREE_VIEW_GRID_LINE_WIDTH; + } + + /* Update the padding for the column */ + _gtk_tree_view_column_push_padding (column, padding); + new_width = _gtk_tree_view_column_get_requested_width (column); + + if (new_width > original_width) + retval = TRUE; + } + + gtk_style_context_restore (context); + + if (draw_hgrid_lines) + height += _TREE_VIEW_GRID_LINE_WIDTH; + + if (height != GTK_TREE_RBNODE_GET_HEIGHT (node)) + { + retval = TRUE; + gtk_tree_rbtree_node_set_height (tree, node, height); + } + gtk_tree_rbtree_node_mark_valid (tree, node); + + return retval; +} + + +static void +validate_visible_area (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreePath *path = NULL; + GtkTreePath *above_path = NULL; + GtkTreeIter iter; + GtkTreeRBTree *tree = NULL; + GtkTreeRBNode *node = NULL; + gboolean need_redraw = FALSE; + gboolean size_changed = FALSE; + int total_height; + int area_above = 0; + int area_below = 0; + + if (priv->tree == NULL) + return; + + if (! GTK_TREE_RBNODE_FLAG_SET (priv->tree->root, GTK_TREE_RBNODE_DESCENDANTS_INVALID) && + priv->scroll_to_path == NULL) + return; + + total_height = gtk_widget_get_height (GTK_WIDGET (tree_view)) + - gtk_tree_view_get_effective_header_height (tree_view); + + if (total_height == 0) + return; + + /* First, we check to see if we need to scroll anywhere + */ + if (priv->scroll_to_path) + { + path = gtk_tree_row_reference_get_path (priv->scroll_to_path); + if (path && !_gtk_tree_view_find_node (tree_view, path, &tree, &node)) + { + /* we are going to scroll, and will update dy */ + gtk_tree_model_get_iter (priv->model, &iter, path); + if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_INVALID) || + GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_COLUMN_INVALID)) + { + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + if (validate_row (tree_view, tree, node, &iter, path)) + size_changed = TRUE; + } + + if (priv->scroll_to_use_align) + { + int height = gtk_tree_view_get_row_height (tree_view, node); + area_above = (total_height - height) * + priv->scroll_to_row_align; + area_below = total_height - area_above - height; + area_above = MAX (area_above, 0); + area_below = MAX (area_below, 0); + } + else + { + /* two cases: + * 1) row not visible + * 2) row visible + */ + int dy; + int height = gtk_tree_view_get_row_height (tree_view, node); + + dy = gtk_tree_rbtree_node_find_offset (tree, node); + + if (dy >= gtk_adjustment_get_value (priv->vadjustment) && + dy + height <= (gtk_adjustment_get_value (priv->vadjustment) + + gtk_adjustment_get_page_size (priv->vadjustment))) + { + /* row visible: keep the row at the same position */ + area_above = dy - gtk_adjustment_get_value (priv->vadjustment); + area_below = (gtk_adjustment_get_value (priv->vadjustment) + + gtk_adjustment_get_page_size (priv->vadjustment)) + - dy - height; + } + else + { + /* row not visible */ + if (dy >= 0 + && dy + height <= gtk_adjustment_get_page_size (priv->vadjustment)) + { + /* row at the beginning -- fixed */ + area_above = dy; + area_below = gtk_adjustment_get_page_size (priv->vadjustment) + - area_above - height; + } + else if (dy >= (gtk_adjustment_get_upper (priv->vadjustment) - + gtk_adjustment_get_page_size (priv->vadjustment))) + { + /* row at the end -- fixed */ + area_above = dy - (gtk_adjustment_get_upper (priv->vadjustment) - + gtk_adjustment_get_page_size (priv->vadjustment)); + area_below = gtk_adjustment_get_page_size (priv->vadjustment) - + area_above - height; + + if (area_below < 0) + { + area_above = gtk_adjustment_get_page_size (priv->vadjustment) - height; + area_below = 0; + } + } + else + { + /* row somewhere in the middle, bring it to the top + * of the view + */ + area_above = 0; + area_below = total_height - height; + } + } + } + } + else + /* the scroll to isn't valid; ignore it. + */ + { + if (priv->scroll_to_path && !path) + { + gtk_tree_row_reference_free (priv->scroll_to_path); + priv->scroll_to_path = NULL; + } + if (path) + gtk_tree_path_free (path); + path = NULL; + } + } + + /* We didn't have a scroll_to set, so we just handle things normally + */ + if (path == NULL) + { + int offset; + + offset = gtk_tree_rbtree_find_offset (priv->tree, + TREE_WINDOW_Y_TO_RBTREE_Y (priv, 0), + &tree, &node); + if (node == NULL) + { + /* In this case, nothing has been validated */ + path = gtk_tree_path_new_first (); + _gtk_tree_view_find_node (tree_view, path, &tree, &node); + } + else + { + path = _gtk_tree_path_new_from_rbtree (tree, node); + total_height += offset; + } + + gtk_tree_model_get_iter (priv->model, &iter, path); + + if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_INVALID) || + GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_COLUMN_INVALID)) + { + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + if (validate_row (tree_view, tree, node, &iter, path)) + size_changed = TRUE; + } + area_above = 0; + area_below = total_height - gtk_tree_view_get_row_height (tree_view, node); + } + + above_path = gtk_tree_path_copy (path); + + /* if we do not validate any row above the new top_row, we will make sure + * that the row immediately above top_row has been validated. (if we do not + * do this, gtk_tree_rbtree_find_offset will find the row above top_row, because + * when invalidated that row's height will be zero. and this will mess up + * scrolling). + */ + if (area_above == 0) + { + GtkTreeRBTree *tmptree; + GtkTreeRBNode *tmpnode; + + _gtk_tree_view_find_node (tree_view, above_path, &tmptree, &tmpnode); + gtk_tree_rbtree_prev_full (tmptree, tmpnode, &tmptree, &tmpnode); + + if (tmpnode) + { + GtkTreePath *tmppath; + GtkTreeIter tmpiter; + + tmppath = _gtk_tree_path_new_from_rbtree (tmptree, tmpnode); + gtk_tree_model_get_iter (priv->model, &tmpiter, tmppath); + + if (GTK_TREE_RBNODE_FLAG_SET (tmpnode, GTK_TREE_RBNODE_INVALID) || + GTK_TREE_RBNODE_FLAG_SET (tmpnode, GTK_TREE_RBNODE_COLUMN_INVALID)) + { + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + if (validate_row (tree_view, tmptree, tmpnode, &tmpiter, tmppath)) + size_changed = TRUE; + } + + gtk_tree_path_free (tmppath); + } + } + + /* Now, we walk forwards and backwards, measuring rows. Unfortunately, + * backwards is much slower then forward, as there is no iter_prev function. + * We go forwards first in case we run out of tree. Then we go backwards to + * fill out the top. + */ + while (node && area_below > 0) + { + if (node->children) + { + GtkTreeIter parent = iter; + gboolean has_child; + + tree = node->children; + node = gtk_tree_rbtree_first (tree); + + has_child = gtk_tree_model_iter_children (priv->model, + &iter, + &parent); + TREE_VIEW_INTERNAL_ASSERT_VOID (has_child); + gtk_tree_path_down (path); + } + else + { + gboolean done = FALSE; + do + { + node = gtk_tree_rbtree_next (tree, node); + if (node != NULL) + { + gboolean has_next = gtk_tree_model_iter_next (priv->model, &iter); + done = TRUE; + gtk_tree_path_next (path); + + /* Sanity Check! */ + TREE_VIEW_INTERNAL_ASSERT_VOID (has_next); + } + else + { + GtkTreeIter parent_iter = iter; + gboolean has_parent; + + node = tree->parent_node; + tree = tree->parent_tree; + if (tree == NULL) + break; + has_parent = gtk_tree_model_iter_parent (priv->model, + &iter, + &parent_iter); + gtk_tree_path_up (path); + + /* Sanity check */ + TREE_VIEW_INTERNAL_ASSERT_VOID (has_parent); + } + } + while (!done); + } + + if (!node) + break; + + if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_INVALID) || + GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_COLUMN_INVALID)) + { + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + if (validate_row (tree_view, tree, node, &iter, path)) + size_changed = TRUE; + } + + area_below -= gtk_tree_view_get_row_height (tree_view, node); + } + gtk_tree_path_free (path); + + /* If we ran out of tree, and have extra area_below left, we need to add it + * to area_above */ + if (area_below > 0) + area_above += area_below; + + _gtk_tree_view_find_node (tree_view, above_path, &tree, &node); + + /* We walk backwards */ + while (area_above > 0) + { + gtk_tree_rbtree_prev_full (tree, node, &tree, &node); + + /* Always find the new path in the tree. We cannot just assume + * a gtk_tree_path_prev() is enough here, as there might be children + * in between this node and the previous sibling node. If this + * appears to be a performance hotspot in profiles, we can look into + * intrigate logic for keeping path, node and iter in sync like + * we do for forward walks. (Which will be hard because of the lacking + * iter_prev). + */ + + if (node == NULL) + break; + + gtk_tree_path_free (above_path); + above_path = _gtk_tree_path_new_from_rbtree (tree, node); + + gtk_tree_model_get_iter (priv->model, &iter, above_path); + + if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_INVALID) || + GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_COLUMN_INVALID)) + { + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + if (validate_row (tree_view, tree, node, &iter, above_path)) + size_changed = TRUE; + } + area_above -= gtk_tree_view_get_row_height (tree_view, node); + } + + /* if we scrolled to a path, we need to set the dy here, + * and sync the top row accordingly + */ + if (priv->scroll_to_path) + { + gtk_tree_view_set_top_row (tree_view, above_path, -area_above); + gtk_tree_view_top_row_to_dy (tree_view); + + need_redraw = TRUE; + } + else if (gtk_tree_view_get_height (tree_view) <= gtk_adjustment_get_page_size (priv->vadjustment)) + { + /* when we are not scrolling, we should never set dy to something + * else than zero. we update top_row to be in sync with dy = 0. + */ + gtk_adjustment_set_value (GTK_ADJUSTMENT (priv->vadjustment), 0); + gtk_tree_view_dy_to_top_row (tree_view); + } + else if (gtk_adjustment_get_value (priv->vadjustment) + gtk_adjustment_get_page_size (priv->vadjustment) > gtk_tree_view_get_height (tree_view)) + { + gtk_adjustment_set_value (GTK_ADJUSTMENT (priv->vadjustment), gtk_tree_view_get_height (tree_view) - gtk_adjustment_get_page_size (priv->vadjustment)); + gtk_tree_view_dy_to_top_row (tree_view); + } + else + gtk_tree_view_top_row_to_dy (tree_view); + + /* update width/height and queue a resize */ + if (size_changed) + { + GtkRequisition requisition; + + /* We temporarily guess a size, under the assumption that it will be the + * same when we get our next size_allocate. If we don't do this, we'll be + * in an inconsistent state if we call top_row_to_dy. */ + + gtk_widget_get_preferred_size (GTK_WIDGET (tree_view), + &requisition, NULL); + gtk_adjustment_set_upper (priv->hadjustment, + MAX (gtk_adjustment_get_upper (priv->hadjustment), requisition.width)); + gtk_adjustment_set_upper (priv->vadjustment, + MAX (gtk_adjustment_get_upper (priv->vadjustment), requisition.height)); + gtk_widget_queue_resize (GTK_WIDGET (tree_view)); + } + + if (priv->scroll_to_path) + { + gtk_tree_row_reference_free (priv->scroll_to_path); + priv->scroll_to_path = NULL; + } + + if (above_path) + gtk_tree_path_free (above_path); + + if (priv->scroll_to_column) + { + priv->scroll_to_column = NULL; + } + if (need_redraw) + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); +} + +static void +initialize_fixed_height_mode (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + if (!priv->tree) + return; + + if (priv->fixed_height < 0) + { + GtkTreeIter iter; + GtkTreePath *path; + + GtkTreeRBTree *tree = NULL; + GtkTreeRBNode *node = NULL; + + tree = priv->tree; + node = tree->root; + + path = _gtk_tree_path_new_from_rbtree (tree, node); + gtk_tree_model_get_iter (priv->model, &iter, path); + + validate_row (tree_view, tree, node, &iter, path); + + gtk_tree_path_free (path); + + priv->fixed_height = gtk_tree_view_get_row_height (tree_view, node); + } + + gtk_tree_rbtree_set_fixed_height (priv->tree, + priv->fixed_height, TRUE); +} + +/* Our strategy for finding nodes to validate is a little convoluted. We find + * the left-most uninvalidated node. We then try walking right, validating + * nodes. Once we find a valid node, we repeat the previous process of finding + * the first invalid node. + */ + +static gboolean +do_validate_rows (GtkTreeView *tree_view, gboolean queue_resize) +{ + static gboolean prevent_recursion_hack = FALSE; + + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreeRBTree *tree = NULL; + GtkTreeRBNode *node = NULL; + gboolean validated_area = FALSE; + int retval = TRUE; + GtkTreePath *path = NULL; + GtkTreeIter iter; + GTimer *timer; + int i = 0; + + int y = -1; + int prev_height = -1; + gboolean fixed_height = TRUE; + + g_assert (tree_view); + + /* prevent infinite recursion via get_preferred_width() */ + if (prevent_recursion_hack) + return FALSE; + + if (priv->tree == NULL) + return FALSE; + + if (priv->fixed_height_mode) + { + if (priv->fixed_height < 0) + initialize_fixed_height_mode (tree_view); + + return FALSE; + } + + timer = g_timer_new (); + g_timer_start (timer); + + do + { + gboolean changed = FALSE; + + if (! GTK_TREE_RBNODE_FLAG_SET (priv->tree->root, GTK_TREE_RBNODE_DESCENDANTS_INVALID)) + { + retval = FALSE; + goto done; + } + + if (path != NULL) + { + node = gtk_tree_rbtree_next (tree, node); + if (node != NULL) + { + TREE_VIEW_INTERNAL_ASSERT (gtk_tree_model_iter_next (priv->model, &iter), FALSE); + gtk_tree_path_next (path); + } + else + { + gtk_tree_path_free (path); + path = NULL; + } + } + + if (path == NULL) + { + tree = priv->tree; + node = priv->tree->root; + + g_assert (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_DESCENDANTS_INVALID)); + + do + { + if (!gtk_tree_rbtree_is_nil (node->left) && + GTK_TREE_RBNODE_FLAG_SET (node->left, GTK_TREE_RBNODE_DESCENDANTS_INVALID)) + { + node = node->left; + } + else if (!gtk_tree_rbtree_is_nil (node->right) && + GTK_TREE_RBNODE_FLAG_SET (node->right, GTK_TREE_RBNODE_DESCENDANTS_INVALID)) + { + node = node->right; + } + else if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_INVALID) || + GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_COLUMN_INVALID)) + { + break; + } + else if (node->children != NULL) + { + tree = node->children; + node = tree->root; + } + else + /* RBTree corruption! All bad */ + g_assert_not_reached (); + } + while (TRUE); + path = _gtk_tree_path_new_from_rbtree (tree, node); + gtk_tree_model_get_iter (priv->model, &iter, path); + } + + changed = validate_row (tree_view, tree, node, &iter, path); + validated_area = changed || validated_area; + + if (changed) + { + int offset = gtk_tree_view_get_row_y_offset (tree_view, tree, node); + + if (y == -1 || y > offset) + y = offset; + } + + if (!priv->fixed_height_check) + { + int height; + + height = gtk_tree_view_get_row_height (tree_view, node); + if (prev_height < 0) + prev_height = height; + else if (prev_height != height) + fixed_height = FALSE; + } + + i++; + } + while (g_timer_elapsed (timer, NULL) < GTK_TREE_VIEW_TIME_MS_PER_IDLE / 1000.); + + if (!priv->fixed_height_check) + { + if (fixed_height) + gtk_tree_rbtree_set_fixed_height (priv->tree, prev_height, FALSE); + + priv->fixed_height_check = 1; + } + + done: + if (validated_area) + { + GtkRequisition requisition; + int dummy; + + /* We temporarily guess a size, under the assumption that it will be the + * same when we get our next size_allocate. If we don't do this, we'll be + * in an inconsistent state when we call top_row_to_dy. */ + + /* FIXME: This is called from size_request, for some reason it is not infinitely + * recursing, we cannot call gtk_widget_get_preferred_size() here because that's + * not allowed (from inside ->get_preferred_width/height() implementations, one + * should call the vfuncs directly). However what is desired here is the full + * size including any margins and limited by any alignment (i.e. after + * GtkWidget:adjust_size_request() is called). + * + * Currently bypassing this but the real solution is to not update the scroll adjustments + * until we've received an allocation (never update scroll adjustments from size-requests). + */ + prevent_recursion_hack = TRUE; + gtk_tree_view_measure (GTK_WIDGET (tree_view), + GTK_ORIENTATION_HORIZONTAL, + -1, + &requisition.width, &dummy, + NULL, NULL); + gtk_tree_view_measure (GTK_WIDGET (tree_view), + GTK_ORIENTATION_VERTICAL, + -1, + &requisition.height, &dummy, + NULL, NULL); + prevent_recursion_hack = FALSE; + + /* If rows above the current position have changed height, this has + * affected the current view and thus needs a redraw. + */ + if (y != -1 && y < gtk_adjustment_get_value (priv->vadjustment)) + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + + gtk_adjustment_set_upper (priv->hadjustment, + MAX (gtk_adjustment_get_upper (priv->hadjustment), requisition.width)); + gtk_adjustment_set_upper (priv->vadjustment, + MAX (gtk_adjustment_get_upper (priv->vadjustment), requisition.height)); + + if (queue_resize) + gtk_widget_queue_resize (GTK_WIDGET (tree_view)); + } + + if (path) gtk_tree_path_free (path); + g_timer_destroy (timer); + + if (!retval && gtk_widget_get_mapped (GTK_WIDGET (tree_view))) + update_prelight (tree_view, + priv->event_last_x, + priv->event_last_y); + + return retval; +} + +static void +disable_adjustment_animation (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + gtk_adjustment_enable_animation (priv->vadjustment, + NULL, + gtk_adjustment_get_animation_duration (priv->vadjustment)); +} + +static void +maybe_reenable_adjustment_animation (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + if (priv->presize_handler_tick_cb != 0 || + priv->validate_rows_timer != 0) + return; + + gtk_adjustment_enable_animation (priv->vadjustment, + gtk_widget_get_frame_clock (GTK_WIDGET (tree_view)), + gtk_adjustment_get_animation_duration (priv->vadjustment)); +} + +static gboolean +do_presize_handler (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + if (priv->mark_rows_col_dirty) + { + if (priv->tree) + gtk_tree_rbtree_column_invalid (priv->tree); + priv->mark_rows_col_dirty = FALSE; + } + validate_visible_area (tree_view); + if (priv->presize_handler_tick_cb != 0) + { + gtk_widget_remove_tick_callback (GTK_WIDGET (tree_view), priv->presize_handler_tick_cb); + priv->presize_handler_tick_cb = 0; + } + + if (priv->fixed_height_mode) + { + GtkRequisition requisition; + + gtk_widget_get_preferred_size (GTK_WIDGET (tree_view), + &requisition, NULL); + + gtk_adjustment_set_upper (priv->hadjustment, + MAX (gtk_adjustment_get_upper (priv->hadjustment), requisition.width)); + gtk_adjustment_set_upper (priv->vadjustment, + MAX (gtk_adjustment_get_upper (priv->vadjustment), requisition.height)); + gtk_widget_queue_resize (GTK_WIDGET (tree_view)); + } + + maybe_reenable_adjustment_animation (tree_view); + + return FALSE; +} + +static gboolean +presize_handler_callback (GtkWidget *widget, + GdkFrameClock *clock, + gpointer unused) +{ + do_presize_handler (GTK_TREE_VIEW (widget)); + + return G_SOURCE_REMOVE; +} + +static gboolean +validate_rows (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + gboolean retval; + + if (priv->presize_handler_tick_cb) + { + do_presize_handler (tree_view); + return G_SOURCE_CONTINUE; + } + + retval = do_validate_rows (tree_view, TRUE); + + if (! retval && priv->validate_rows_timer) + { + g_source_remove (priv->validate_rows_timer); + priv->validate_rows_timer = 0; + maybe_reenable_adjustment_animation (tree_view); + } + + return retval; +} + +static void +install_presize_handler (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + if (! gtk_widget_get_realized (GTK_WIDGET (tree_view))) + return; + + disable_adjustment_animation (tree_view); + + if (! priv->presize_handler_tick_cb) + { + priv->presize_handler_tick_cb = + gtk_widget_add_tick_callback (GTK_WIDGET (tree_view), presize_handler_callback, NULL, NULL); + } + if (! priv->validate_rows_timer) + { + priv->validate_rows_timer = + g_idle_add_full (GTK_TREE_VIEW_PRIORITY_VALIDATE, (GSourceFunc) validate_rows, tree_view, NULL); + gdk_source_set_static_name_by_id (priv->validate_rows_timer, "[gtk] validate_rows"); + } +} + +static gboolean +scroll_sync_handler (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + if (gtk_tree_view_get_height (tree_view) <= gtk_adjustment_get_page_size (priv->vadjustment)) + gtk_adjustment_set_value (GTK_ADJUSTMENT (priv->vadjustment), 0); + else if (gtk_tree_row_reference_valid (priv->top_row)) + gtk_tree_view_top_row_to_dy (tree_view); + else + gtk_tree_view_dy_to_top_row (tree_view); + + priv->scroll_sync_timer = 0; + + return FALSE; +} + +static void +install_scroll_sync_handler (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + if (!gtk_widget_get_realized (GTK_WIDGET (tree_view))) + return; + + if (!priv->scroll_sync_timer) + { + priv->scroll_sync_timer = + g_idle_add_full (GTK_TREE_VIEW_PRIORITY_SCROLL_SYNC, (GSourceFunc) scroll_sync_handler, tree_view, NULL); + gdk_source_set_static_name_by_id (priv->scroll_sync_timer, "[gtk] scroll_sync_handler"); + } +} + +static void +gtk_tree_view_set_top_row (GtkTreeView *tree_view, + GtkTreePath *path, + int offset) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + gtk_tree_row_reference_free (priv->top_row); + + if (!path) + { + priv->top_row = NULL; + priv->top_row_dy = 0; + } + else + { + priv->top_row = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), priv->model, path); + priv->top_row_dy = offset; + } +} + +/* Always call this iff dy is in the visible range. If the tree is empty, then + * it’s set to be NULL, and top_row_dy is 0; + */ +static void +gtk_tree_view_dy_to_top_row (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + int offset; + GtkTreePath *path; + GtkTreeRBTree *tree; + GtkTreeRBNode *node; + + if (priv->tree == NULL) + { + gtk_tree_view_set_top_row (tree_view, NULL, 0); + } + else + { + offset = gtk_tree_rbtree_find_offset (priv->tree, + priv->dy, + &tree, &node); + + if (tree == NULL) + { + gtk_tree_view_set_top_row (tree_view, NULL, 0); + } + else + { + path = _gtk_tree_path_new_from_rbtree (tree, node); + gtk_tree_view_set_top_row (tree_view, path, offset); + gtk_tree_path_free (path); + } + } +} + +static void +gtk_tree_view_top_row_to_dy (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreePath *path; + GtkTreeRBTree *tree; + GtkTreeRBNode *node; + int new_dy; + + /* Avoid recursive calls */ + if (priv->in_top_row_to_dy) + return; + + if (gtk_adjustment_is_animating (priv->vadjustment)) + return; + + if (priv->top_row) + path = gtk_tree_row_reference_get_path (priv->top_row); + else + path = NULL; + + if (!path) + tree = NULL; + else + _gtk_tree_view_find_node (tree_view, path, &tree, &node); + + if (path) + gtk_tree_path_free (path); + + if (tree == NULL) + { + /* keep dy and set new toprow */ + gtk_tree_row_reference_free (priv->top_row); + priv->top_row = NULL; + priv->top_row_dy = 0; + /* DO NOT install the idle handler */ + gtk_tree_view_dy_to_top_row (tree_view); + return; + } + + if (gtk_tree_view_get_row_height (tree_view, node) + < priv->top_row_dy) + { + /* new top row -- do NOT install the idle handler */ + gtk_tree_view_dy_to_top_row (tree_view); + return; + } + + new_dy = gtk_tree_rbtree_node_find_offset (tree, node); + new_dy += priv->top_row_dy; + + if (new_dy + gtk_adjustment_get_page_size (priv->vadjustment) > gtk_tree_view_get_height (tree_view)) + new_dy = gtk_tree_view_get_height (tree_view) - gtk_adjustment_get_page_size (priv->vadjustment); + + new_dy = MAX (0, new_dy); + + priv->in_top_row_to_dy = TRUE; + gtk_adjustment_set_value (priv->vadjustment, (double)new_dy); + priv->in_top_row_to_dy = FALSE; +} + + +void +_gtk_tree_view_install_mark_rows_col_dirty (GtkTreeView *tree_view, + gboolean install_handler) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + priv->mark_rows_col_dirty = TRUE; + + if (install_handler) + install_presize_handler (tree_view); +} + +/* + * This function works synchronously (due to the while (validate_rows...) + * loop). + * + * There was a check for column_type != GTK_TREE_VIEW_COLUMN_AUTOSIZE + * here. You now need to check that yourself. + */ +void +_gtk_tree_view_column_autosize (GtkTreeView *tree_view, + GtkTreeViewColumn *column) +{ + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (column)); + + _gtk_tree_view_column_cell_set_dirty (column, FALSE); + + do_presize_handler (tree_view); + while (validate_rows (tree_view)); + + gtk_widget_queue_resize (GTK_WIDGET (tree_view)); +} + +/* Drag-and-drop */ + +typedef struct +{ + GtkTreeRowReference *dest_row; + guint path_down_mode : 1; + guint empty_view_drop : 1; + guint drop_append_mode : 1; +} +DestRow; + +static void +dest_row_free (gpointer data) +{ + DestRow *dr = (DestRow *)data; + + gtk_tree_row_reference_free (dr->dest_row); + g_slice_free (DestRow, dr); +} + +static void +set_dest_row (GdkDrop *drop, + GtkTreeModel *model, + GtkTreePath *dest_row, + gboolean path_down_mode, + gboolean empty_view_drop, + gboolean drop_append_mode) +{ + DestRow *dr; + + if (!dest_row) + { + g_object_set_data_full (G_OBJECT (drop), I_("gtk-tree-view-dest-row"), + NULL, NULL); + return; + } + + dr = g_slice_new (DestRow); + + dr->dest_row = gtk_tree_row_reference_new (model, dest_row); + dr->path_down_mode = path_down_mode != FALSE; + dr->empty_view_drop = empty_view_drop != FALSE; + dr->drop_append_mode = drop_append_mode != FALSE; + + g_object_set_data_full (G_OBJECT (drop), I_("gtk-tree-view-dest-row"), + dr, (GDestroyNotify) dest_row_free); +} + +static GtkTreePath* +get_dest_row (GdkDrop *drop, + gboolean *path_down_mode) +{ + DestRow *dr = + g_object_get_data (G_OBJECT (drop), "gtk-tree-view-dest-row"); + + if (dr) + { + GtkTreePath *path = NULL; + + if (path_down_mode) + *path_down_mode = dr->path_down_mode; + + if (dr->dest_row) + path = gtk_tree_row_reference_get_path (dr->dest_row); + else if (dr->empty_view_drop) + path = gtk_tree_path_new_from_indices (0, -1); + else + path = NULL; + + if (path && dr->drop_append_mode) + gtk_tree_path_next (path); + + return path; + } + else + return NULL; +} + +/* Get/set whether drag_motion requested the drag data and + * drag_data_received should thus not actually insert the data, + * since the data doesn’t result from a drop. + */ +static void +set_status_pending (GdkDrop *drop, + GdkDragAction suggested_action) +{ + g_object_set_data (G_OBJECT (drop), + I_("gtk-tree-view-status-pending"), + GINT_TO_POINTER (suggested_action)); +} + +static GdkDragAction +get_status_pending (GdkDrop *drop) +{ + return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (drop), + "gtk-tree-view-status-pending")); +} + +static TreeViewDragInfo* +get_info (GtkTreeView *tree_view) +{ + return g_object_get_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info"); +} + +static void +destroy_info (TreeViewDragInfo *di) +{ + g_clear_pointer (&di->source_formats, gdk_content_formats_unref); + g_clear_pointer (&di->source_item, gtk_tree_row_reference_free); + g_clear_object (&di->dest); + + g_slice_free (TreeViewDragInfo, di); +} + +static TreeViewDragInfo* +ensure_info (GtkTreeView *tree_view) +{ + TreeViewDragInfo *di; + + di = get_info (tree_view); + + if (di == NULL) + { + di = g_slice_new0 (TreeViewDragInfo); + + g_object_set_data_full (G_OBJECT (tree_view), + I_("gtk-tree-view-drag-info"), + di, + (GDestroyNotify) destroy_info); + } + + return di; +} + +static void +remove_info (GtkTreeView *tree_view) +{ + TreeViewDragInfo *di; + + di = get_info (tree_view); + if (di && di->dest) + gtk_widget_remove_controller (GTK_WIDGET (tree_view), GTK_EVENT_CONTROLLER (di->dest)); + g_object_set_data (G_OBJECT (tree_view), I_("gtk-tree-view-drag-info"), NULL); +} + +static void +add_scroll_timeout (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + if (priv->scroll_timeout == 0) + { + priv->scroll_timeout = g_timeout_add (150, scroll_row_timeout, tree_view); + gdk_source_set_static_name_by_id (priv->scroll_timeout, "[gtk] scroll_row_timeout"); + } +} + +static void +remove_scroll_timeout (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_clear_handle_id (&priv->scroll_timeout, g_source_remove); +} + +static gboolean +check_model_dnd (GtkTreeModel *model, + GType required_iface, + const char *signal) +{ + if (model == NULL || !G_TYPE_CHECK_INSTANCE_TYPE ((model), required_iface)) + { + g_warning ("You must override the default '%s' handler " + "on GtkTreeView when using models that don't support " + "the %s interface and enabling drag-and-drop. The simplest way to do this " + "is to connect to '%s' and call " + "g_signal_stop_emission_by_name() in your signal handler to prevent " + "the default handler from running. Look at the source code " + "for the default handler in gtktreeview.c to get an idea what " + "your handler should do. (gtktreeview.c is in the GTK source " + "code.) If you're using GTK from a language other than C, " + "there may be a more natural way to override default handlers, e.g. via derivation.", + signal, g_type_name (required_iface), signal); + return FALSE; + } + else + return TRUE; +} + +static void +remove_open_timeout (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_clear_handle_id (&priv->open_dest_timeout, g_source_remove); +} + + +static int +open_row_timeout (gpointer data) +{ + GtkTreeView *tree_view = data; + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreePath *dest_path = NULL; + GtkTreeViewDropPosition pos; + gboolean result = FALSE; + + gtk_tree_view_get_drag_dest_row (tree_view, + &dest_path, + &pos); + + if (dest_path && + (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER || + pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) + { + gtk_tree_view_expand_row (tree_view, dest_path, FALSE); + priv->open_dest_timeout = 0; + + gtk_tree_path_free (dest_path); + } + else + { + if (dest_path) + gtk_tree_path_free (dest_path); + + result = TRUE; + } + + return result; +} + +static gboolean +scroll_row_timeout (gpointer data) +{ + GtkTreeView *tree_view = data; + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + gtk_tree_view_vertical_autoscroll (tree_view); + + if (priv->rubber_band_status == RUBBER_BAND_ACTIVE) + gtk_tree_view_update_rubber_band (tree_view); + + return TRUE; +} + +static GdkDragAction +gtk_tree_view_get_action (GtkWidget *widget, + GdkDrop *drop) +{ + GtkTreeView *tree_view = GTK_TREE_VIEW (widget); + TreeViewDragInfo *di; + GdkDrag *drag = gdk_drop_get_drag (drop); + GdkDragAction actions; + + di = get_info (tree_view); + + actions = gdk_drop_get_actions (drop); + + if (di && di->drag == drag && + actions & GDK_ACTION_MOVE) + return GDK_ACTION_MOVE; + + if (actions & GDK_ACTION_COPY) + return GDK_ACTION_COPY; + + if (actions & GDK_ACTION_MOVE) + return GDK_ACTION_MOVE; + + return 0; +} + +/* Returns TRUE if event should not be propagated to parent widgets */ +static gboolean +set_destination_row (GtkTreeView *tree_view, + GdkDrop *drop, + GtkDropTargetAsync *dest, + /* coordinates relative to the widget */ + int x, + int y, + GdkDragAction *suggested_action, + GType *target) +{ + GtkTreePath *path = NULL; + GtkTreeViewDropPosition pos; + GtkTreeViewDropPosition old_pos; + TreeViewDragInfo *di; + GtkWidget *widget; + GtkTreePath *old_dest_path = NULL; + gboolean can_drop = FALSE; + GdkContentFormats *formats; + + *suggested_action = 0; + *target = G_TYPE_INVALID; + + widget = GTK_WIDGET (tree_view); + + di = get_info (tree_view); + + if (di == NULL || y - gtk_tree_view_get_effective_header_height (tree_view) < 0) + { + /* someone unset us as a drag dest, note that if + * we return FALSE drag_leave isn't called + */ + + gtk_tree_view_set_drag_dest_row (tree_view, + NULL, + GTK_TREE_VIEW_DROP_BEFORE); + + remove_scroll_timeout (GTK_TREE_VIEW (widget)); + remove_open_timeout (GTK_TREE_VIEW (widget)); + + return FALSE; /* no longer a drop site */ + } + + formats = gtk_drop_target_async_get_formats (dest); + *target = gdk_content_formats_match_gtype (formats, formats); + if (*target == G_TYPE_INVALID) + return FALSE; + + if (!gtk_tree_view_get_dest_row_at_pos (tree_view, + x, y, + &path, + &pos)) + { + int n_children; + GtkTreeModel *model; + + remove_open_timeout (tree_view); + + /* the row got dropped on empty space, let's setup a special case + */ + + if (path) + gtk_tree_path_free (path); + + model = gtk_tree_view_get_model (tree_view); + + n_children = gtk_tree_model_iter_n_children (model, NULL); + if (n_children) + { + pos = GTK_TREE_VIEW_DROP_AFTER; + path = gtk_tree_path_new_from_indices (n_children - 1, -1); + } + else + { + pos = GTK_TREE_VIEW_DROP_BEFORE; + path = gtk_tree_path_new_from_indices (0, -1); + } + + can_drop = TRUE; + + goto out; + } + + g_assert (path); + + /* If we left the current row's "open" zone, unset the timeout for + * opening the row + */ + gtk_tree_view_get_drag_dest_row (tree_view, + &old_dest_path, + &old_pos); + + if (old_dest_path && + (gtk_tree_path_compare (path, old_dest_path) != 0 || + !(pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER || + pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE))) + remove_open_timeout (tree_view); + + if (old_dest_path) + gtk_tree_path_free (old_dest_path); + + if (TRUE /* FIXME if the location droppable predicate */) + { + can_drop = TRUE; + } + +out: + if (can_drop) + { + *suggested_action = gtk_tree_view_get_action (widget, drop); + + gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), + path, pos); + } + else + { + /* can't drop here */ + remove_open_timeout (tree_view); + + gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), + NULL, + GTK_TREE_VIEW_DROP_BEFORE); + } + + if (path) + gtk_tree_path_free (path); + + return TRUE; +} + +static GtkTreePath* +get_logical_dest_row (GtkTreeView *tree_view, + gboolean *path_down_mode, + gboolean *drop_append_mode) +{ + /* adjust path to point to the row the drop goes in front of */ + GtkTreePath *path = NULL; + GtkTreeViewDropPosition pos; + + g_return_val_if_fail (path_down_mode != NULL, NULL); + g_return_val_if_fail (drop_append_mode != NULL, NULL); + + *path_down_mode = FALSE; + *drop_append_mode = 0; + + gtk_tree_view_get_drag_dest_row (tree_view, &path, &pos); + + if (path == NULL) + return NULL; + + if (pos == GTK_TREE_VIEW_DROP_BEFORE) + ; /* do nothing */ + else if (pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE || + pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER) + *path_down_mode = TRUE; + else + { + GtkTreeIter iter; + GtkTreeModel *model = gtk_tree_view_get_model (tree_view); + + g_assert (pos == GTK_TREE_VIEW_DROP_AFTER); + + if (!gtk_tree_model_get_iter (model, &iter, path) || + !gtk_tree_model_iter_next (model, &iter)) + *drop_append_mode = 1; + else + { + *drop_append_mode = 0; + gtk_tree_path_next (path); + } + } + + return path; +} + +static gboolean +gtk_tree_view_maybe_begin_dragging_row (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkWidget *widget = GTK_WIDGET (tree_view); + double start_x, start_y, offset_x, offset_y; + TreeViewDragInfo *di; + GtkTreePath *path = NULL; + int button; + GtkTreeModel *model; + gboolean retval = FALSE; + int bin_x, bin_y; + GdkSurface *surface; + GdkDevice *device; + GdkContentProvider *content; + GdkDrag *drag; + GdkPaintable *icon; + + di = get_info (tree_view); + + if (di == NULL || !di->source_set) + goto out; + + if (!gtk_gesture_is_recognized (priv->drag_gesture)) + goto out; + + gtk_gesture_drag_get_start_point (GTK_GESTURE_DRAG (priv->drag_gesture), + &start_x, &start_y); + gtk_gesture_drag_get_offset (GTK_GESTURE_DRAG (priv->drag_gesture), + &offset_x, &offset_y); + + if (!gtk_drag_check_threshold_double (widget, 0, 0, offset_x, offset_y)) + goto out; + + model = gtk_tree_view_get_model (tree_view); + + if (model == NULL) + goto out; + + button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (priv->drag_gesture)); + + /* Deny the click gesture */ + gtk_gesture_set_state (GTK_GESTURE (priv->click_gesture), + GTK_EVENT_SEQUENCE_DENIED); + + gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, start_x, start_y, + &bin_x, &bin_y); + gtk_tree_view_get_path_at_pos (tree_view, bin_x, bin_y, &path, + NULL, NULL, NULL); + + if (path == NULL) + goto out; + + if (!GTK_IS_TREE_DRAG_SOURCE (model) || + !gtk_tree_drag_source_row_draggable (GTK_TREE_DRAG_SOURCE (model), + path)) + goto out; + + if (!(GDK_BUTTON1_MASK << (button - 1) & di->start_button_mask)) + goto out; + + /* Now we can begin the drag */ + gtk_gesture_set_state (GTK_GESTURE (priv->drag_gesture), + GTK_EVENT_SEQUENCE_CLAIMED); + + surface = gtk_native_get_surface (gtk_widget_get_native (GTK_WIDGET (tree_view))); + device = gtk_gesture_get_device (GTK_GESTURE (priv->drag_gesture)), + content = gtk_tree_view_drag_data_get (tree_view, path); + if (content == NULL) + goto out; + + retval = TRUE; + + drag = gdk_drag_begin (surface, device, content, di->source_actions, start_x, start_y); + + g_object_unref (content); + + g_signal_connect (drag, "dnd-finished", G_CALLBACK (gtk_tree_view_dnd_finished_cb), tree_view); + + icon = gtk_tree_view_create_row_drag_icon (tree_view, path); + gtk_drag_icon_set_from_paintable (drag, icon, priv->press_start_x + 1, 1); + g_object_unref (icon); + + di->drag = drag; + + g_object_unref (drag); + + di->source_item = gtk_tree_row_reference_new (model, path); + + out: + if (path) + gtk_tree_path_free (path); + + return retval; +} + +static void +gtk_tree_view_dnd_finished_cb (GdkDrag *drag, + GtkWidget *widget) +{ + GtkTreeView *tree_view = GTK_TREE_VIEW (widget); + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + TreeViewDragInfo *di; + GtkTreeModel *model; + GtkTreePath *source_row; + + priv->event_last_x = -10000; + priv->event_last_y = -10000; + + if (gdk_drag_get_selected_action (drag) != GDK_ACTION_MOVE) + return; + + tree_view = GTK_TREE_VIEW (widget); + model = gtk_tree_view_get_model (tree_view); + + if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_SOURCE, "drag_data_delete")) + return; + + di = get_info (tree_view); + + if (di == NULL || di->source_item == NULL) + return; + + source_row = gtk_tree_row_reference_get_path (di->source_item); + + if (source_row == NULL) + return; + + gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (model), source_row); + + gtk_tree_path_free (source_row); + + g_clear_pointer (&di->source_item, gtk_tree_row_reference_free); +} + +/* Default signal implementations for the drag signals */ +static GdkContentProvider * +gtk_tree_view_drag_data_get (GtkTreeView *tree_view, + GtkTreePath *source_row) +{ + GtkTreeModel *model; + GdkContentProvider *content; + + model = gtk_tree_view_get_model (tree_view); + + if (model == NULL) + return NULL; + + /* We can implement the GTK_TREE_MODEL_ROW target generically for + * any model; for DragSource models there are some other targets + * we also support. + */ + + if (GTK_IS_TREE_DRAG_SOURCE (model)) + content = gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (model), source_row); + else + content = NULL; + + /* If drag_data_get does nothing, try providing row data. */ + if (!content) + content = gtk_tree_create_row_drag_content (model, source_row); + + return content; +} + +static void +gtk_tree_view_drag_leave (GtkDropTargetAsync *dest, + GdkDrop *drop, + GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + /* unset any highlight row */ + gtk_tree_view_set_drag_dest_row (tree_view, + NULL, + GTK_TREE_VIEW_DROP_BEFORE); + + remove_scroll_timeout (tree_view); + remove_open_timeout (tree_view); + + priv->event_last_x = -10000; + priv->event_last_y = -10000; +} + + +static GdkDragAction +gtk_tree_view_drag_motion (GtkDropTargetAsync *dest, + GdkDrop *drop, + double x, + double y, + GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + gboolean empty; + GtkTreePath *path = NULL; + GtkTreeViewDropPosition pos; + GdkDragAction suggested_action = 0; + GType target; + + if (!set_destination_row (tree_view, drop, dest, x, y, &suggested_action, &target)) + return 0; + + priv->event_last_x = x; + priv->event_last_y = y; + + gtk_tree_view_get_drag_dest_row (tree_view, &path, &pos); + + /* we only know this *after* set_desination_row */ + empty = priv->empty_view_drop; + + if (path == NULL && !empty) + { + suggested_action = 0; + } + else + { + if (priv->open_dest_timeout == 0 && + (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER || + pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) + { + priv->open_dest_timeout = + g_timeout_add (AUTO_EXPAND_TIMEOUT, open_row_timeout, tree_view); + gdk_source_set_static_name_by_id (priv->open_dest_timeout, "[gtk] open_row_timeout"); + } + else + { + add_scroll_timeout (tree_view); + } + + if (target == GTK_TYPE_TREE_ROW_DATA) + { + /* Request data so we can use the source row when + * determining whether to accept the drop + */ + set_status_pending (drop, suggested_action); + gdk_drop_read_value_async (drop, GTK_TYPE_TREE_ROW_DATA, G_PRIORITY_DEFAULT, NULL, gtk_tree_view_drag_data_received, tree_view); + } + else + { + set_status_pending (drop, 0); + } + } + + if (path) + gtk_tree_path_free (path); + + return suggested_action; +} + + +static gboolean +gtk_tree_view_drag_drop (GtkDropTargetAsync *dest, + GdkDrop *drop, + double x, + double y, + GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreePath *path; + GdkDragAction suggested_action = 0; + GType target = G_TYPE_INVALID; + TreeViewDragInfo *di; + GtkTreeModel *model; + gboolean path_down_mode; + gboolean drop_append_mode; + + model = gtk_tree_view_get_model (tree_view); + + remove_scroll_timeout (tree_view); + remove_open_timeout (tree_view); + + di = get_info (tree_view); + + if (di == NULL) + return FALSE; + + if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_drop")) + return FALSE; + + if (!set_destination_row (tree_view, drop, dest, x, y, &suggested_action, &target)) + return FALSE; + + path = get_logical_dest_row (tree_view, &path_down_mode, &drop_append_mode); + + if (target != G_TYPE_INVALID && path != NULL) + { + /* in case a motion had requested drag data, change things so we + * treat drag data receives as a drop. + */ + set_status_pending (drop, 0); + set_dest_row (drop, model, path, + path_down_mode, priv->empty_view_drop, + drop_append_mode); + } + + if (path) + gtk_tree_path_free (path); + + /* Unset this thing */ + gtk_tree_view_set_drag_dest_row (tree_view, + NULL, + GTK_TREE_VIEW_DROP_BEFORE); + + if (target != G_TYPE_INVALID) + { + gdk_drop_read_value_async (drop, GTK_TYPE_TREE_ROW_DATA, G_PRIORITY_DEFAULT, NULL, gtk_tree_view_drag_data_received, tree_view); + return TRUE; + } + else + return FALSE; +} + +static void +gtk_tree_view_drag_data_received (GObject *source, + GAsyncResult *result, + gpointer data) +{ + GtkTreeView *tree_view = GTK_TREE_VIEW (data); + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GdkDrop *drop = GDK_DROP (source); + GtkTreePath *path; + TreeViewDragInfo *di; + GtkTreeModel *model; + GtkTreePath *dest_row; + GdkDragAction suggested_action; + gboolean path_down_mode; + gboolean drop_append_mode; + const GValue *value; + + value = gdk_drop_read_value_finish (drop, result, NULL); + if (value == NULL) + return; + + model = gtk_tree_view_get_model (tree_view); + + if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_data_received")) + return; + + di = get_info (tree_view); + + if (di == NULL) + return; + + suggested_action = get_status_pending (drop); + + if (suggested_action) + { + /* We are getting this data due to a request in drag_motion, + * rather than due to a request in drag_drop, so we are just + * supposed to call drag_status, not actually paste in the + * data. + */ + path = get_logical_dest_row (tree_view, &path_down_mode, + &drop_append_mode); + + if (path == NULL) + suggested_action = 0; + else if (path_down_mode) + gtk_tree_path_down (path); + + if (suggested_action) + { + if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model), + path, + value)) + { + if (path_down_mode) + { + path_down_mode = FALSE; + gtk_tree_path_up (path); + + if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model), + path, + value)) + suggested_action = 0; + } + else + suggested_action = 0; + } + } + + if (path) + gtk_tree_path_free (path); + + /* If you can't drop, remove user drop indicator until the next motion */ + if (suggested_action == 0) + gtk_tree_view_set_drag_dest_row (tree_view, + NULL, + GTK_TREE_VIEW_DROP_BEFORE); + + return; + } + + dest_row = get_dest_row (drop, &path_down_mode); + + if (dest_row == NULL) + return; + + if (path_down_mode) + { + gtk_tree_path_down (dest_row); + if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model), + dest_row, value)) + gtk_tree_path_up (dest_row); + } + + suggested_action = gtk_tree_view_get_action (GTK_WIDGET (tree_view), drop); + + if (suggested_action && + !gtk_tree_drag_dest_drag_data_received (GTK_TREE_DRAG_DEST (model), + dest_row, + value)) + suggested_action = 0; + + gdk_drop_finish (drop, suggested_action); + + if (gtk_tree_path_get_depth (dest_row) == 1 && + gtk_tree_path_get_indices (dest_row)[0] == 0 && + gtk_tree_model_iter_n_children (priv->model, NULL) != 0) + { + /* special case drag to "0", scroll to first item */ + if (!priv->scroll_to_path) + gtk_tree_view_scroll_to_cell (tree_view, dest_row, NULL, FALSE, 0.0, 0.0); + } + + gtk_tree_path_free (dest_row); + + /* drop dest_row */ + set_dest_row (drop, NULL, NULL, FALSE, FALSE, FALSE); +} + +static void +gtk_tree_view_remove (GtkTreeView *tree_view, + GtkWidget *widget) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreeViewChild *child = NULL; + GList *tmp_list; + + tmp_list = priv->children; + while (tmp_list) + { + child = tmp_list->data; + if (child->widget == widget) + { + gtk_widget_unparent (widget); + + priv->children = g_list_remove_link (priv->children, tmp_list); + g_list_free_1 (tmp_list); + g_slice_free (GtkTreeViewChild, child); + return; + } + + tmp_list = tmp_list->next; + } + + tmp_list = priv->columns; + + while (tmp_list) + { + GtkTreeViewColumn *column; + GtkWidget *button; + + column = tmp_list->data; + button = gtk_tree_view_column_get_button (column); + + if (button == widget) + { + gtk_widget_unparent (widget); + return; + } + tmp_list = tmp_list->next; + } +} + +/* Returns TRUE is any of the columns contains a cell that can-focus. + * If this is not the case, a column-spanning focus rectangle will be + * drawn. + */ +static gboolean +gtk_tree_view_has_can_focus_cell (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GList *list; + + for (list = priv->columns; list; list = list->next) + { + GtkTreeViewColumn *column = list->data; + + if (!gtk_tree_view_column_get_visible (column)) + continue; + if (gtk_cell_area_is_activatable (gtk_cell_layout_get_area (GTK_CELL_LAYOUT (column)))) + return TRUE; + } + + return FALSE; +} + +static void +column_sizing_notify (GObject *object, + GParamSpec *pspec, + gpointer data) +{ + GtkTreeViewColumn *c = GTK_TREE_VIEW_COLUMN (object); + + if (gtk_tree_view_column_get_sizing (c) != GTK_TREE_VIEW_COLUMN_FIXED) + /* disable fixed height mode */ + g_object_set (data, "fixed-height-mode", FALSE, NULL); +} + +/** + * gtk_tree_view_set_fixed_height_mode: + * @tree_view: a `GtkTreeView` + * @enable: %TRUE to enable fixed height mode + * + * Enables or disables the fixed height mode of @tree_view. + * Fixed height mode speeds up `GtkTreeView` by assuming that all + * rows have the same height. + * Only enable this option if all rows are the same height and all + * columns are of type %GTK_TREE_VIEW_COLUMN_FIXED. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +void +gtk_tree_view_set_fixed_height_mode (GtkTreeView *tree_view, + gboolean enable) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GList *l; + + enable = enable != FALSE; + + if (enable == priv->fixed_height_mode) + return; + + if (!enable) + { + priv->fixed_height_mode = 0; + priv->fixed_height = -1; + } + else + { + /* make sure all columns are of type FIXED */ + for (l = priv->columns; l; l = l->next) + { + g_return_if_fail (gtk_tree_view_column_get_sizing (l->data) == GTK_TREE_VIEW_COLUMN_FIXED); + } + + /* yes, we really have to do this is in a separate loop */ + for (l = priv->columns; l; l = l->next) + g_signal_connect (l->data, "notify::sizing", + G_CALLBACK (column_sizing_notify), tree_view); + + priv->fixed_height_mode = 1; + priv->fixed_height = -1; + } + + /* force a revalidation */ + install_presize_handler (tree_view); + + g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_FIXED_HEIGHT_MODE]); +} + +/** + * gtk_tree_view_get_fixed_height_mode: + * @tree_view: a `GtkTreeView` + * + * Returns whether fixed height mode is turned on for @tree_view. + * + * Returns: %TRUE if @tree_view is in fixed height mode + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +gboolean +gtk_tree_view_get_fixed_height_mode (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + return priv->fixed_height_mode; +} + +/* Returns TRUE if the focus is within the headers, after the focus operation is + * done + */ +static gboolean +gtk_tree_view_header_focus (GtkTreeView *tree_view, + GtkDirectionType dir, + gboolean clamp_column_visible) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreeViewColumn *column; + GtkWidget *button; + GtkWidget *focus_child; + GList *last_column, *first_column; + GList *tmp_list; + gboolean rtl; + + if (! priv->headers_visible) + return FALSE; + + focus_child = gtk_widget_get_focus_child (GTK_WIDGET (tree_view)); + + first_column = priv->columns; + while (first_column) + { + column = GTK_TREE_VIEW_COLUMN (first_column->data); + button = gtk_tree_view_column_get_button (column); + + if (gtk_widget_get_focusable (button) && + gtk_tree_view_column_get_visible (column) && + (gtk_tree_view_column_get_clickable (column) || + gtk_tree_view_column_get_reorderable (column))) + break; + first_column = first_column->next; + } + + /* No headers are visible, or are focusable. We can't focus in or out. + */ + if (first_column == NULL) + return FALSE; + + last_column = g_list_last (priv->columns); + while (last_column) + { + column = GTK_TREE_VIEW_COLUMN (last_column->data); + button = gtk_tree_view_column_get_button (column); + + if (gtk_widget_get_focusable (button) && + gtk_tree_view_column_get_visible (column) && + (gtk_tree_view_column_get_clickable (column) || + gtk_tree_view_column_get_reorderable (column))) + break; + last_column = last_column->prev; + } + + + rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); + + switch (dir) + { + case GTK_DIR_TAB_BACKWARD: + case GTK_DIR_TAB_FORWARD: + case GTK_DIR_UP: + case GTK_DIR_DOWN: + if (focus_child == NULL) + { + if (priv->focus_column != NULL) + button = gtk_tree_view_column_get_button (priv->focus_column); + else + button = NULL; + + if (button && gtk_widget_get_focusable (button)) + focus_child = button; + else + focus_child = gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (first_column->data)); + + gtk_widget_grab_focus (focus_child); + break; + } + return FALSE; + + case GTK_DIR_LEFT: + case GTK_DIR_RIGHT: + if (focus_child == NULL) + { + if (priv->focus_column != NULL) + focus_child = gtk_tree_view_column_get_button (priv->focus_column); + else if (dir == GTK_DIR_LEFT) + focus_child = gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (last_column->data)); + else + focus_child = gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (first_column->data)); + + gtk_widget_grab_focus (focus_child); + break; + } + + if (gtk_widget_child_focus (focus_child, dir)) + { + /* The focus moves inside the button. */ + /* This is probably a great example of bad UI */ + break; + } + + /* We need to move the focus among the row of buttons. */ + for (tmp_list = priv->columns; tmp_list; tmp_list = tmp_list->next) + if (gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (tmp_list->data)) == focus_child) + break; + + if ((tmp_list == first_column && dir == (rtl ? GTK_DIR_RIGHT : GTK_DIR_LEFT)) + || (tmp_list == last_column && dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT))) + { + gtk_widget_error_bell (GTK_WIDGET (tree_view)); + break; + } + + while (tmp_list) + { + if (dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT)) + tmp_list = tmp_list->next; + else + tmp_list = tmp_list->prev; + + if (tmp_list == NULL) + { + g_warning ("Internal button not found"); + break; + } + column = tmp_list->data; + button = gtk_tree_view_column_get_button (column); + if (button && + gtk_tree_view_column_get_visible (column) && + gtk_widget_get_focusable (button)) + { + focus_child = button; + gtk_widget_grab_focus (button); + break; + } + } + break; + default: + g_assert_not_reached (); + break; + } + + /* if focus child is non-null, we assume it's been set to the current focus child + */ + if (focus_child) + { + for (tmp_list = priv->columns; tmp_list; tmp_list = tmp_list->next) + if (gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (tmp_list->data)) == focus_child) + { + _gtk_tree_view_set_focus_column (tree_view, GTK_TREE_VIEW_COLUMN (tmp_list->data)); + break; + } + + if (clamp_column_visible) + { + gtk_tree_view_clamp_column_visible (tree_view, + priv->focus_column, + FALSE); + } + } + + return (focus_child != NULL); +} + +/* This function returns in 'path' the first focusable path, if the given path + * is already focusable, it’s the returned one. + */ +static gboolean +search_first_focusable_path (GtkTreeView *tree_view, + GtkTreePath **path, + gboolean search_forward, + GtkTreeRBTree **new_tree, + GtkTreeRBNode **new_node) +{ + GtkTreeRBTree *tree = NULL; + GtkTreeRBNode *node = NULL; + + if (!path || !*path) + return FALSE; + + _gtk_tree_view_find_node (tree_view, *path, &tree, &node); + + if (!tree || !node) + return FALSE; + + while (node && row_is_separator (tree_view, NULL, *path)) + { + if (search_forward) + gtk_tree_rbtree_next_full (tree, node, &tree, &node); + else + gtk_tree_rbtree_prev_full (tree, node, &tree, &node); + + if (*path) + gtk_tree_path_free (*path); + + if (node) + *path = _gtk_tree_path_new_from_rbtree (tree, node); + else + *path = NULL; + } + + if (new_tree) + *new_tree = tree; + + if (new_node) + *new_node = node; + + return (*path != NULL); +} + +static int +gtk_tree_view_focus (GtkWidget *widget, + GtkDirectionType direction) +{ + GtkTreeView *tree_view = GTK_TREE_VIEW (widget); + GtkWidget *focus_child; + + focus_child = gtk_widget_get_focus_child (widget); + + gtk_tree_view_stop_editing (GTK_TREE_VIEW (widget), FALSE); + /* Case 1. Headers currently have focus. */ + if (focus_child) + { + switch (direction) + { + case GTK_DIR_LEFT: + case GTK_DIR_RIGHT: + gtk_tree_view_header_focus (tree_view, direction, TRUE); + return TRUE; + case GTK_DIR_TAB_BACKWARD: + case GTK_DIR_UP: + return FALSE; + case GTK_DIR_TAB_FORWARD: + case GTK_DIR_DOWN: + return gtk_widget_grab_focus (widget); + default: + g_assert_not_reached (); + return FALSE; + } + } + + /* Case 2. We don't have focus at all. */ + if (!gtk_widget_has_focus (widget)) + { + return gtk_widget_grab_focus (widget); + } + + /* Case 3. We have focus already. */ + if (direction == GTK_DIR_TAB_BACKWARD) + return (gtk_tree_view_header_focus (tree_view, direction, FALSE)); + else if (direction == GTK_DIR_TAB_FORWARD) + return FALSE; + + /* Other directions caught by the keybindings */ + return gtk_widget_grab_focus (widget); +} + +static gboolean +gtk_tree_view_grab_focus (GtkWidget *widget) +{ + if (!gtk_widget_grab_focus_self (widget)) + return FALSE; + + gtk_tree_view_focus_to_cursor (GTK_TREE_VIEW (widget)); + return TRUE; +} + +static void +gtk_tree_view_css_changed (GtkWidget *widget, + GtkCssStyleChange *change) +{ + GtkTreeView *tree_view = GTK_TREE_VIEW (widget); + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GList *list; + GtkTreeViewColumn *column; + + GTK_WIDGET_CLASS (gtk_tree_view_parent_class)->css_changed (widget, change); + + if (gtk_widget_get_realized (widget)) + { + gtk_tree_view_set_grid_lines (tree_view, priv->grid_lines); + gtk_tree_view_set_enable_tree_lines (tree_view, priv->tree_lines_enabled); + } + + if (change == NULL || gtk_css_style_change_affects (change, GTK_CSS_AFFECTS_SIZE)) + { + for (list = priv->columns; list; list = list->next) + { + column = list->data; + _gtk_tree_view_column_cell_set_dirty (column, TRUE); + } + + priv->fixed_height = -1; + gtk_tree_rbtree_mark_invalid (priv->tree); + } + + /* Invalidate expander size */ + priv->expander_size = -1; +} + +static gboolean +gtk_tree_view_real_move_cursor (GtkTreeView *tree_view, + GtkMovementStep step, + int count, + gboolean extend, + gboolean modify) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); + g_return_val_if_fail (step == GTK_MOVEMENT_LOGICAL_POSITIONS || + step == GTK_MOVEMENT_VISUAL_POSITIONS || + step == GTK_MOVEMENT_DISPLAY_LINES || + step == GTK_MOVEMENT_PAGES || + step == GTK_MOVEMENT_BUFFER_ENDS, FALSE); + + if (priv->tree == NULL) + return FALSE; + if (!gtk_widget_has_focus (GTK_WIDGET (tree_view))) + return FALSE; + + gtk_tree_view_stop_editing (tree_view, FALSE); + priv->draw_keyfocus = TRUE; + gtk_widget_grab_focus (GTK_WIDGET (tree_view)); + + priv->modify_selection_pressed = modify; + priv->extend_selection_pressed = extend; + + switch (step) + { + /* currently we make no distinction. When we go bi-di, we need to */ + case GTK_MOVEMENT_LOGICAL_POSITIONS: + case GTK_MOVEMENT_VISUAL_POSITIONS: + gtk_tree_view_move_cursor_left_right (tree_view, count); + break; + case GTK_MOVEMENT_DISPLAY_LINES: + gtk_tree_view_move_cursor_up_down (tree_view, count); + break; + case GTK_MOVEMENT_PAGES: + gtk_tree_view_move_cursor_page_up_down (tree_view, count); + break; + case GTK_MOVEMENT_BUFFER_ENDS: + gtk_tree_view_move_cursor_start_end (tree_view, count); + break; + case GTK_MOVEMENT_WORDS: + case GTK_MOVEMENT_DISPLAY_LINE_ENDS: + case GTK_MOVEMENT_PARAGRAPHS: + case GTK_MOVEMENT_PARAGRAPH_ENDS: + case GTK_MOVEMENT_HORIZONTAL_PAGES: + default: + g_assert_not_reached (); + } + + priv->modify_selection_pressed = FALSE; + priv->extend_selection_pressed = FALSE; + + return TRUE; +} + +static void +gtk_tree_view_put (GtkTreeView *tree_view, + GtkWidget *child_widget, + GtkTreePath *path, + GtkTreeViewColumn *column, + const GtkBorder *border) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreeViewChild *child; + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + g_return_if_fail (GTK_IS_WIDGET (child_widget)); + + child = g_slice_new (GtkTreeViewChild); + + child->widget = child_widget; + if (_gtk_tree_view_find_node (tree_view, + path, + &child->tree, + &child->node)) + { + g_assert_not_reached (); + } + child->column = column; + child->border = *border; + + priv->children = g_list_append (priv->children, child); + + gtk_css_node_insert_after (gtk_widget_get_css_node (GTK_WIDGET (tree_view)), + gtk_widget_get_css_node (child_widget), + priv->header_node); + gtk_widget_set_parent (child_widget, GTK_WIDGET (tree_view)); +} + +/* TreeModel Callbacks + */ + +static void +gtk_tree_view_row_changed (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + GtkTreeView *tree_view = (GtkTreeView *)data; + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreeRBTree *tree; + GtkTreeRBNode *node; + gboolean free_path = FALSE; + GList *list; + GtkTreePath *cursor_path; + + g_return_if_fail (path != NULL || iter != NULL); + + if (priv->cursor_node != NULL) + cursor_path = _gtk_tree_path_new_from_rbtree (priv->cursor_tree, + priv->cursor_node); + else + cursor_path = NULL; + + if (priv->edited_column && + (cursor_path == NULL || gtk_tree_path_compare (cursor_path, path) == 0)) + gtk_tree_view_stop_editing (tree_view, TRUE); + + if (cursor_path != NULL) + gtk_tree_path_free (cursor_path); + + if (path == NULL) + { + path = gtk_tree_model_get_path (model, iter); + free_path = TRUE; + } + else if (iter == NULL) + gtk_tree_model_get_iter (model, iter, path); + + if (_gtk_tree_view_find_node (tree_view, + path, + &tree, + &node)) + /* We aren't actually showing the node */ + goto done; + + if (tree == NULL) + goto done; + + if (priv->fixed_height_mode + && priv->fixed_height >= 0) + { + gtk_tree_rbtree_node_set_height (tree, node, priv->fixed_height); + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + } + else + { + gtk_tree_rbtree_node_mark_invalid (tree, node); + for (list = priv->columns; list; list = list->next) + { + GtkTreeViewColumn *column; + + column = list->data; + if (!gtk_tree_view_column_get_visible (column)) + continue; + + if (gtk_tree_view_column_get_sizing (column) == GTK_TREE_VIEW_COLUMN_AUTOSIZE) + { + _gtk_tree_view_column_cell_set_dirty (column, TRUE); + } + } + } + + done: + if (!priv->fixed_height_mode && + gtk_widget_get_realized (GTK_WIDGET (tree_view))) + install_presize_handler (tree_view); + if (free_path) + gtk_tree_path_free (path); +} + +static void +gtk_tree_view_row_inserted (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + GtkTreeView *tree_view = (GtkTreeView *) data; + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + int *indices; + GtkTreeRBTree *tree; + GtkTreeRBNode *tmpnode = NULL; + int depth; + int i = 0; + int height; + gboolean free_path = FALSE; + + g_return_if_fail (path != NULL || iter != NULL); + + if (priv->fixed_height_mode + && priv->fixed_height >= 0) + height = priv->fixed_height; + else + height = 0; + + if (path == NULL) + { + path = gtk_tree_model_get_path (model, iter); + free_path = TRUE; + } + else if (iter == NULL) + gtk_tree_model_get_iter (model, iter, path); + + if (priv->tree == NULL) + priv->tree = gtk_tree_rbtree_new (); + + tree = priv->tree; + + /* Update all row-references */ + gtk_tree_row_reference_inserted (G_OBJECT (data), path); + depth = gtk_tree_path_get_depth (path); + indices = gtk_tree_path_get_indices (path); + + /* First, find the parent tree */ + while (i < depth - 1) + { + if (tree == NULL) + { + /* We aren't showing the node */ + goto done; + } + + tmpnode = gtk_tree_rbtree_find_count (tree, indices[i] + 1); + if (tmpnode == NULL) + { + g_warning ("A node was inserted with a parent that's not in the tree.\n" \ + "This possibly means that a GtkTreeModel inserted a child node\n" \ + "before the parent was inserted."); + goto done; + } + else if (!GTK_TREE_RBNODE_FLAG_SET (tmpnode, GTK_TREE_RBNODE_IS_PARENT)) + { + /* FIXME enforce correct behavior on model, probably */ + /* In theory, the model should have emitted has_child_toggled here. We + * try to catch it anyway, just to be safe, in case the model hasn't. + */ + GtkTreePath *tmppath = _gtk_tree_path_new_from_rbtree (tree, tmpnode); + gtk_tree_view_row_has_child_toggled (model, tmppath, NULL, data); + gtk_tree_path_free (tmppath); + goto done; + } + + tree = tmpnode->children; + i++; + } + + if (tree == NULL) + { + goto done; + } + + /* ref the node */ + gtk_tree_model_ref_node (priv->model, iter); + if (indices[depth - 1] == 0) + { + tmpnode = gtk_tree_rbtree_find_count (tree, 1); + tmpnode = gtk_tree_rbtree_insert_before (tree, tmpnode, height, FALSE); + } + else + { + tmpnode = gtk_tree_rbtree_find_count (tree, indices[depth - 1]); + tmpnode = gtk_tree_rbtree_insert_after (tree, tmpnode, height, FALSE); + } + + done: + if (height > 0) + { + if (tree) + gtk_tree_rbtree_node_mark_valid (tree, tmpnode); + + gtk_widget_queue_resize (GTK_WIDGET (tree_view)); + } + else + install_presize_handler (tree_view); + if (free_path) + gtk_tree_path_free (path); +} + +static void +gtk_tree_view_row_has_child_toggled (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + GtkTreeView *tree_view = (GtkTreeView *)data; + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreeIter real_iter; + gboolean has_child; + GtkTreeRBTree *tree; + GtkTreeRBNode *node; + gboolean free_path = FALSE; + + g_return_if_fail (path != NULL || iter != NULL); + + if (iter) + real_iter = *iter; + + if (path == NULL) + { + path = gtk_tree_model_get_path (model, iter); + free_path = TRUE; + } + else if (iter == NULL) + gtk_tree_model_get_iter (model, &real_iter, path); + + if (_gtk_tree_view_find_node (tree_view, + path, + &tree, + &node)) + /* We aren't actually showing the node */ + goto done; + + if (tree == NULL) + goto done; + + has_child = gtk_tree_model_iter_has_child (model, &real_iter); + /* Sanity check. + */ + if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_PARENT) == has_child) + goto done; + + if (has_child) + { + GTK_TREE_RBNODE_SET_FLAG (node, GTK_TREE_RBNODE_IS_PARENT); + } + else + { + GTK_TREE_RBNODE_UNSET_FLAG (node, GTK_TREE_RBNODE_IS_PARENT); + } + + if (has_child && priv->is_list) + { + priv->is_list = FALSE; + if (priv->show_expanders) + { + GList *list; + + for (list = priv->columns; list; list = list->next) + if (gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (list->data))) + { + _gtk_tree_view_column_cell_set_dirty (GTK_TREE_VIEW_COLUMN (list->data), TRUE); + break; + } + } + gtk_widget_queue_resize (GTK_WIDGET (tree_view)); + } + else + { + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + } + + done: + if (free_path) + gtk_tree_path_free (path); +} + +static void +check_selection_helper (GtkTreeRBTree *tree, + GtkTreeRBNode *node, + gpointer data) +{ + int *value = (int *)data; + + *value |= GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED); + + if (node->children && !*value) + gtk_tree_rbtree_traverse (node->children, node->children->root, G_POST_ORDER, check_selection_helper, data); +} + +static void +gtk_tree_view_row_deleted (GtkTreeModel *model, + GtkTreePath *path, + gpointer data) +{ + GtkTreeView *tree_view = (GtkTreeView *)data; + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreeRBTree *tree; + GtkTreeRBNode *node; + GList *list; + gboolean selection_changed = FALSE, cursor_changed = FALSE; + GtkTreeRBTree *cursor_tree = NULL; + GtkTreeRBNode *cursor_node = NULL; + + g_return_if_fail (path != NULL); + + gtk_tree_row_reference_deleted (G_OBJECT (data), path); + + if (_gtk_tree_view_find_node (tree_view, path, &tree, &node)) + return; + + if (tree == NULL) + return; + + /* check if the selection has been changed */ + gtk_tree_rbtree_traverse (tree, node, G_POST_ORDER, + check_selection_helper, &selection_changed); + + for (list = priv->columns; list; list = list->next) + if (gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (list->data)) && + gtk_tree_view_column_get_sizing (GTK_TREE_VIEW_COLUMN (list->data)) == GTK_TREE_VIEW_COLUMN_AUTOSIZE) + _gtk_tree_view_column_cell_set_dirty ((GtkTreeViewColumn *)list->data, TRUE); + + /* Ensure we don't have a dangling pointer to a dead node */ + ensure_unprelighted (tree_view); + + /* Cancel editing if we've started */ + gtk_tree_view_stop_editing (tree_view, TRUE); + + /* If the cursor row got deleted, move the cursor to the next row */ + if (priv->cursor_node && + (priv->cursor_node == node || + (node->children && (priv->cursor_tree == node->children || + gtk_tree_rbtree_contains (node->children, priv->cursor_tree))))) + { + GtkTreePath *cursor_path; + + cursor_tree = tree; + cursor_node = gtk_tree_rbtree_next (tree, node); + /* find the first node that is not going to be deleted */ + while (cursor_node == NULL && cursor_tree->parent_tree) + { + cursor_node = gtk_tree_rbtree_next (cursor_tree->parent_tree, + cursor_tree->parent_node); + cursor_tree = cursor_tree->parent_tree; + } + + if (cursor_node != NULL) + cursor_path = _gtk_tree_path_new_from_rbtree (cursor_tree, cursor_node); + else + cursor_path = NULL; + + if (cursor_path == NULL || + ! search_first_focusable_path (tree_view, &cursor_path, TRUE, + &cursor_tree, &cursor_node)) + { + /* It looks like we reached the end of the view without finding + * a focusable row. We will step backwards to find the last + * focusable row. + */ + gtk_tree_rbtree_prev_full (tree, node, &cursor_tree, &cursor_node); + if (cursor_node) + { + cursor_path = _gtk_tree_path_new_from_rbtree (cursor_tree, cursor_node); + if (! search_first_focusable_path (tree_view, &cursor_path, FALSE, + &cursor_tree, &cursor_node)) + cursor_node = NULL; + gtk_tree_path_free (cursor_path); + } + } + else if (cursor_path) + gtk_tree_path_free (cursor_path); + + cursor_changed = TRUE; + } + + if (tree->root->count == 1) + { + if (priv->tree == tree) + priv->tree = NULL; + + gtk_tree_rbtree_remove (tree); + } + else + { + gtk_tree_rbtree_remove_node (tree, node); + } + + if (! gtk_tree_row_reference_valid (priv->top_row)) + { + gtk_tree_row_reference_free (priv->top_row); + priv->top_row = NULL; + } + + install_scroll_sync_handler (tree_view); + + gtk_widget_queue_resize (GTK_WIDGET (tree_view)); + + if (cursor_changed) + { + if (cursor_node) + { + GtkTreePath *cursor_path = _gtk_tree_path_new_from_rbtree (cursor_tree, cursor_node); + gtk_tree_view_real_set_cursor (tree_view, cursor_path, CLEAR_AND_SELECT | CURSOR_INVALID); + gtk_tree_path_free (cursor_path); + } + else + gtk_tree_view_real_set_cursor (tree_view, NULL, CLEAR_AND_SELECT | CURSOR_INVALID); + } + if (selection_changed) + g_signal_emit_by_name (priv->selection, "changed"); +} + +static void +gtk_tree_view_rows_reordered (GtkTreeModel *model, + GtkTreePath *parent, + GtkTreeIter *iter, + int *new_order, + gpointer data) +{ + GtkTreeView *tree_view = GTK_TREE_VIEW (data); + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreeRBTree *tree; + GtkTreeRBNode *node; + int len; + + len = gtk_tree_model_iter_n_children (model, iter); + + if (len < 2) + return; + + gtk_tree_row_reference_reordered (G_OBJECT (data), + parent, + iter, + new_order); + + if (_gtk_tree_view_find_node (tree_view, + parent, + &tree, + &node)) + return; + + /* We need to special case the parent path */ + if (tree == NULL) + tree = priv->tree; + else + tree = node->children; + + if (tree == NULL) + return; + + if (priv->edited_column) + gtk_tree_view_stop_editing (tree_view, TRUE); + + /* we need to be unprelighted */ + ensure_unprelighted (tree_view); + + gtk_tree_rbtree_reorder (tree, new_order, len); + + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + + gtk_tree_view_dy_to_top_row (tree_view); +} + + +/* Internal tree functions + */ + + +static void +gtk_tree_view_get_background_xrange (GtkTreeView *tree_view, + GtkTreeRBTree *tree, + GtkTreeViewColumn *column, + int *x1, + int *x2) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreeViewColumn *tmp_column = NULL; + int total_width; + GList *list; + gboolean rtl; + + if (x1) + *x1 = 0; + + if (x2) + *x2 = 0; + + rtl = (_gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); + + total_width = 0; + for (list = (rtl ? g_list_last (priv->columns) : g_list_first (priv->columns)); + list; + list = (rtl ? list->prev : list->next)) + { + tmp_column = list->data; + + if (tmp_column == column) + break; + + if (gtk_tree_view_column_get_visible (tmp_column)) + total_width += gtk_tree_view_column_get_width (tmp_column); + } + + if (tmp_column != column) + { + g_warning (G_STRLOC": passed-in column isn't in the tree"); + return; + } + + if (x1) + *x1 = total_width; + + if (x2) + { + if (gtk_tree_view_column_get_visible (column)) + *x2 = total_width + gtk_tree_view_column_get_width (column); + else + *x2 = total_width; /* width of 0 */ + } +} + +static void +gtk_tree_view_get_arrow_xrange (GtkTreeView *tree_view, + GtkTreeRBTree *tree, + int *x1, + int *x2) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + int x_offset = 0; + GList *list; + GtkTreeViewColumn *tmp_column = NULL; + int total_width; + int expander_size, expander_render_size; + gboolean rtl; + + rtl = (_gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); + expander_size = gtk_tree_view_get_expander_size (tree_view); + expander_render_size = expander_size - (_TREE_VIEW_HORIZONTAL_SEPARATOR / 2); + + total_width = 0; + for (list = (rtl ? g_list_last (priv->columns) : g_list_first (priv->columns)); + list; + list = (rtl ? list->prev : list->next)) + { + tmp_column = list->data; + + if (gtk_tree_view_is_expander_column (tree_view, tmp_column)) + { + if (rtl) + x_offset = total_width + gtk_tree_view_column_get_width (tmp_column) - expander_size; + else + x_offset = total_width; + break; + } + + if (gtk_tree_view_column_get_visible (tmp_column)) + total_width += gtk_tree_view_column_get_width (tmp_column); + } + + x_offset += (expander_size - expander_render_size); + + if (rtl) + x_offset -= expander_size * gtk_tree_rbtree_get_depth (tree); + else + x_offset += expander_size * gtk_tree_rbtree_get_depth (tree); + + *x1 = x_offset; + + if (tmp_column && + gtk_tree_view_column_get_visible (tmp_column)) + /* +1 because x2 isn't included in the range. */ + *x2 = *x1 + expander_render_size + 1; + else + *x2 = *x1; +} + +static void +gtk_tree_view_build_tree (GtkTreeView *tree_view, + GtkTreeRBTree *tree, + GtkTreeIter *iter, + int depth, + gboolean recurse) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreeRBNode *temp = NULL; + GtkTreePath *path = NULL; + + do + { + gtk_tree_model_ref_node (priv->model, iter); + temp = gtk_tree_rbtree_insert_after (tree, temp, 0, FALSE); + + if (priv->fixed_height > 0) + { + if (GTK_TREE_RBNODE_FLAG_SET (temp, GTK_TREE_RBNODE_INVALID)) + { + gtk_tree_rbtree_node_set_height (tree, temp, priv->fixed_height); + gtk_tree_rbtree_node_mark_valid (tree, temp); + } + } + + if (priv->is_list) + continue; + + if (recurse) + { + GtkTreeIter child; + + if (!path) + path = gtk_tree_model_get_path (priv->model, iter); + else + gtk_tree_path_next (path); + + if (gtk_tree_model_iter_has_child (priv->model, iter)) + { + gboolean expand; + + g_signal_emit (tree_view, tree_view_signals[TEST_EXPAND_ROW], 0, iter, path, &expand); + + if (gtk_tree_model_iter_children (priv->model, &child, iter) + && !expand) + { + temp->children = gtk_tree_rbtree_new (); + temp->children->parent_tree = tree; + temp->children->parent_node = temp; + gtk_tree_view_build_tree (tree_view, temp->children, &child, depth + 1, recurse); + } + } + } + + if (gtk_tree_model_iter_has_child (priv->model, iter)) + { + if ((temp->flags>K_TREE_RBNODE_IS_PARENT) != GTK_TREE_RBNODE_IS_PARENT) + temp->flags ^= GTK_TREE_RBNODE_IS_PARENT; + } + } + while (gtk_tree_model_iter_next (priv->model, iter)); + + if (path) + gtk_tree_path_free (path); +} + +/* Make sure the node is visible vertically */ +static void +gtk_tree_view_clamp_node_visible (GtkTreeView *tree_view, + GtkTreeRBTree *tree, + GtkTreeRBNode *node) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + int node_dy, height; + GtkTreePath *path = NULL; + + if (!gtk_widget_get_realized (GTK_WIDGET (tree_view))) + return; + + /* just return if the node is visible, avoiding a costly expose */ + node_dy = gtk_tree_rbtree_node_find_offset (tree, node); + height = gtk_tree_view_get_row_height (tree_view, node); + if (! GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_INVALID) + && node_dy >= gtk_adjustment_get_value (priv->vadjustment) + && node_dy + height <= (gtk_adjustment_get_value (priv->vadjustment) + + gtk_adjustment_get_page_size (priv->vadjustment))) + return; + + path = _gtk_tree_path_new_from_rbtree (tree, node); + if (path) + { + gtk_tree_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0.0, 0.0); + gtk_tree_path_free (path); + } +} + +static void +gtk_tree_view_clamp_column_visible (GtkTreeView *tree_view, + GtkTreeViewColumn *column, + gboolean focus_to_cell) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkAllocation allocation; + int x, width; + + if (column == NULL) + return; + + gtk_widget_get_allocation (gtk_tree_view_column_get_button (column), &allocation); + x = allocation.x; + width = allocation.width; + + if (width > gtk_adjustment_get_page_size (priv->hadjustment)) + { + /* The column is larger than the horizontal page size. If the + * column has cells which can be focused individually, then we make + * sure the cell which gets focus is fully visible (if even the + * focus cell is bigger than the page size, we make sure the + * left-hand side of the cell is visible). + * + * If the column does not have an activatable cell, we + * make sure the left-hand side of the column is visible. + */ + + if (focus_to_cell && gtk_tree_view_has_can_focus_cell (tree_view)) + { + GtkCellArea *cell_area; + GtkCellRenderer *focus_cell; + + cell_area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (column)); + focus_cell = gtk_cell_area_get_focus_cell (cell_area); + + if (gtk_tree_view_column_cell_get_position (column, focus_cell, + &x, &width)) + { + if (width < gtk_adjustment_get_page_size (priv->hadjustment)) + { + if (gtk_adjustment_get_value (priv->hadjustment) + gtk_adjustment_get_page_size (priv->hadjustment) < x + width) + gtk_adjustment_set_value (priv->hadjustment, + x + width - gtk_adjustment_get_page_size (priv->hadjustment)); + else if (gtk_adjustment_get_value (priv->hadjustment) > x) + gtk_adjustment_set_value (priv->hadjustment, x); + } + } + } + + gtk_adjustment_set_value (priv->hadjustment, x); + } + else + { + if ((gtk_adjustment_get_value (priv->hadjustment) + gtk_adjustment_get_page_size (priv->hadjustment)) < (x + width)) + gtk_adjustment_set_value (priv->hadjustment, + x + width - gtk_adjustment_get_page_size (priv->hadjustment)); + else if (gtk_adjustment_get_value (priv->hadjustment) > x) + gtk_adjustment_set_value (priv->hadjustment, x); + } +} + +/* This function could be more efficient. I'll optimize it if profiling seems + * to imply that it is important */ +GtkTreePath * +_gtk_tree_path_new_from_rbtree (GtkTreeRBTree *tree, + GtkTreeRBNode *node) +{ + GtkTreePath *path; + GtkTreeRBTree *tmp_tree; + GtkTreeRBNode *tmp_node, *last; + int count; + + path = gtk_tree_path_new (); + + g_return_val_if_fail (node != NULL, path); + + count = 1 + node->left->count; + + last = node; + tmp_node = node->parent; + tmp_tree = tree; + while (tmp_tree) + { + while (!gtk_tree_rbtree_is_nil (tmp_node)) + { + if (tmp_node->right == last) + count += 1 + tmp_node->left->count; + last = tmp_node; + tmp_node = tmp_node->parent; + } + gtk_tree_path_prepend_index (path, count - 1); + last = tmp_tree->parent_node; + tmp_tree = tmp_tree->parent_tree; + if (last) + { + count = 1 + last->left->count; + tmp_node = last->parent; + } + } + return path; +} + +/* Returns TRUE if we ran out of tree before finding the path. If the path is + * invalid (ie. points to a node that’s not in the tree), *tree and *node are + * both set to NULL. + */ +gboolean +_gtk_tree_view_find_node (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeRBTree **tree, + GtkTreeRBNode **node) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreeRBNode *tmpnode = NULL; + GtkTreeRBTree *tmptree = priv->tree; + int *indices = gtk_tree_path_get_indices (path); + int depth = gtk_tree_path_get_depth (path); + int i = 0; + + *node = NULL; + *tree = NULL; + + if (depth == 0 || tmptree == NULL) + return FALSE; + do + { + tmpnode = gtk_tree_rbtree_find_count (tmptree, indices[i] + 1); + ++i; + if (tmpnode == NULL) + { + *tree = NULL; + *node = NULL; + return FALSE; + } + if (i >= depth) + { + *tree = tmptree; + *node = tmpnode; + return FALSE; + } + *tree = tmptree; + *node = tmpnode; + tmptree = tmpnode->children; + if (tmptree == NULL) + return TRUE; + } + while (1); +} + +static gboolean +gtk_tree_view_is_expander_column (GtkTreeView *tree_view, + GtkTreeViewColumn *column) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GList *list; + + if (priv->is_list) + return FALSE; + + if (priv->expander_column != NULL) + { + if (priv->expander_column == column) + return TRUE; + return FALSE; + } + else + { + for (list = priv->columns; + list; + list = list->next) + if (gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (list->data))) + break; + if (list && list->data == column) + return TRUE; + } + return FALSE; +} + +static inline gboolean +gtk_tree_view_draw_expanders (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + if (!priv->is_list && priv->show_expanders) + return TRUE; + /* else */ + return FALSE; +} + +static void +gtk_tree_view_add_move_binding (GtkWidgetClass *widget_class, + guint keyval, + guint modmask, + gboolean add_shifted_binding, + GtkMovementStep step, + int count) +{ + gtk_widget_class_add_binding_signal (widget_class, + keyval, modmask, + "move-cursor", + "(iibb)", step, count, FALSE, FALSE); + + if (add_shifted_binding) + gtk_widget_class_add_binding_signal (widget_class, + keyval, GDK_SHIFT_MASK, + "move-cursor", + "(iibb)", step, count, TRUE, FALSE); + + if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK) + return; + + gtk_widget_class_add_binding_signal (widget_class, + keyval, GDK_CONTROL_MASK, + "move-cursor", + "(iibb)", step, count, FALSE, TRUE); + + if (add_shifted_binding) + gtk_widget_class_add_binding_signal (widget_class, keyval, + GDK_CONTROL_MASK | GDK_SHIFT_MASK, + "move-cursor", + "(iibb)", step, count, TRUE, TRUE); +} + +static int +gtk_tree_view_unref_tree_helper (GtkTreeModel *model, + GtkTreeIter *iter, + GtkTreeRBTree *tree, + GtkTreeRBNode *node) +{ + int retval = FALSE; + do + { + g_return_val_if_fail (node != NULL, FALSE); + + if (node->children) + { + GtkTreeIter child; + GtkTreeRBTree *new_tree; + GtkTreeRBNode *new_node; + + new_tree = node->children; + new_node = gtk_tree_rbtree_first (new_tree); + + if (!gtk_tree_model_iter_children (model, &child, iter)) + return FALSE; + + retval = gtk_tree_view_unref_tree_helper (model, &child, new_tree, new_node) | retval; + } + + if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED)) + retval = TRUE; + gtk_tree_model_unref_node (model, iter); + node = gtk_tree_rbtree_next (tree, node); + } + while (gtk_tree_model_iter_next (model, iter)); + + return retval; +} + +static int +gtk_tree_view_unref_and_check_selection_tree (GtkTreeView *tree_view, + GtkTreeRBTree *tree) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreeIter iter; + GtkTreePath *path; + GtkTreeRBNode *node; + int retval; + + if (!tree) + return FALSE; + + node = gtk_tree_rbtree_first (tree); + + g_return_val_if_fail (node != NULL, FALSE); + path = _gtk_tree_path_new_from_rbtree (tree, node); + gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->model), + &iter, path); + retval = gtk_tree_view_unref_tree_helper (GTK_TREE_MODEL (priv->model), &iter, tree, node); + gtk_tree_path_free (path); + + return retval; +} + +static void +gtk_tree_view_set_column_drag_info (GtkTreeView *tree_view, + GtkTreeViewColumn *column) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreeViewColumn *left_column; + GtkTreeViewColumn *cur_column = NULL; + GtkTreeViewColumnReorder *reorder; + gboolean rtl; + GList *tmp_list; + int left; + + /* We want to precalculate the motion list such that we know what column slots + * are available. + */ + left_column = NULL; + rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); + + /* First, identify all possible drop spots */ + if (rtl) + tmp_list = g_list_last (priv->columns); + else + tmp_list = g_list_first (priv->columns); + + while (tmp_list) + { + cur_column = GTK_TREE_VIEW_COLUMN (tmp_list->data); + tmp_list = rtl ? tmp_list->prev : tmp_list->next; + + if (gtk_tree_view_column_get_visible (cur_column) == FALSE) + continue; + + /* If it's not the column moving and func tells us to skip over the column, we continue. */ + if (left_column != column && cur_column != column && + priv->column_drop_func && + ! priv->column_drop_func (tree_view, column, left_column, cur_column, priv->column_drop_func_data)) + { + left_column = cur_column; + continue; + } + reorder = g_slice_new0 (GtkTreeViewColumnReorder); + reorder->left_column = left_column; + left_column = reorder->right_column = cur_column; + + priv->column_drag_info = g_list_append (priv->column_drag_info, reorder); + } + + /* Add the last one */ + if (priv->column_drop_func == NULL || + ((left_column != column) && + priv->column_drop_func (tree_view, column, left_column, NULL, priv->column_drop_func_data))) + { + reorder = g_slice_new0 (GtkTreeViewColumnReorder); + reorder->left_column = left_column; + reorder->right_column = NULL; + priv->column_drag_info = g_list_append (priv->column_drag_info, reorder); + } + + /* We quickly check to see if it even makes sense to reorder columns. */ + /* If there is nothing that can be moved, then we return */ + + if (priv->column_drag_info == NULL) + return; + + /* We know there are always 2 slots possbile, as you can always return column. */ + /* If that's all there is, return */ + if (priv->column_drag_info->next == NULL || + (priv->column_drag_info->next->next == NULL && + ((GtkTreeViewColumnReorder *)priv->column_drag_info->data)->right_column == column && + ((GtkTreeViewColumnReorder *)priv->column_drag_info->next->data)->left_column == column)) + { + for (tmp_list = priv->column_drag_info; tmp_list; tmp_list = tmp_list->next) + g_slice_free (GtkTreeViewColumnReorder, tmp_list->data); + g_list_free (priv->column_drag_info); + priv->column_drag_info = NULL; + return; + } + /* We fill in the ranges for the columns, now that we've isolated them */ + left = - TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view); + + for (tmp_list = priv->column_drag_info; tmp_list; tmp_list = tmp_list->next) + { + reorder = (GtkTreeViewColumnReorder *) tmp_list->data; + + reorder->left_align = left; + if (tmp_list->next != NULL) + { + GtkAllocation right_allocation, left_allocation; + GtkWidget *left_button, *right_button; + + g_assert (tmp_list->next->data); + + right_button = gtk_tree_view_column_get_button (reorder->right_column); + left_button = gtk_tree_view_column_get_button + (((GtkTreeViewColumnReorder *)tmp_list->next->data)->left_column); + + gtk_widget_get_allocation (right_button, &right_allocation); + gtk_widget_get_allocation (left_button, &left_allocation); + left = reorder->right_align = (right_allocation.x + right_allocation.width + left_allocation.x) / 2; + } + else + { + reorder->right_align = gtk_widget_get_allocated_width (GTK_WIDGET (tree_view)) + + TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view); + } + } +} + +void +_gtk_tree_view_column_start_drag (GtkTreeView *tree_view, + GtkTreeViewColumn *column, + GdkDevice *device) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkAllocation button_allocation; + GtkWidget *button; + GtkStyleContext *context; + + g_return_if_fail (priv->column_drag_info == NULL); + g_return_if_fail (priv->cur_reorder == NULL); + + gtk_tree_view_set_column_drag_info (tree_view, column); + + if (priv->column_drag_info == NULL) + return; + + button = gtk_tree_view_column_get_button (column); + + context = gtk_widget_get_style_context (button); + gtk_style_context_add_class (context, "dnd"); + + gtk_widget_get_allocation (button, &button_allocation); + priv->drag_column_x = button_allocation.x; + priv->drag_column_y = button_allocation.y; + + priv->drag_column = column; + + gtk_widget_grab_focus (GTK_WIDGET (tree_view)); + + priv->in_column_drag = TRUE; + + gtk_gesture_set_state (priv->column_drag_gesture, + GTK_EVENT_SEQUENCE_CLAIMED); +} + +static inline int +gtk_tree_view_get_effective_header_height (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + if (priv->headers_visible) + return priv->header_height; + else + return 0; +} + +void +_gtk_tree_view_get_row_separator_func (GtkTreeView *tree_view, + GtkTreeViewRowSeparatorFunc *func, + gpointer *data) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + *func = priv->row_separator_func; + *data = priv->row_separator_data; +} + +GtkTreePath * +_gtk_tree_view_get_anchor_path (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + if (priv->anchor) + return gtk_tree_row_reference_get_path (priv->anchor); + + return NULL; +} + +void +_gtk_tree_view_set_anchor_path (GtkTreeView *tree_view, + GtkTreePath *anchor_path) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + if (priv->anchor) + { + gtk_tree_row_reference_free (priv->anchor); + priv->anchor = NULL; + } + + if (anchor_path && priv->model) + priv->anchor = + gtk_tree_row_reference_new (priv->model, anchor_path); +} + +GtkTreeRBTree * +_gtk_tree_view_get_rbtree (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + return priv->tree; +} + +gboolean +_gtk_tree_view_get_cursor_node (GtkTreeView *tree_view, + GtkTreeRBTree **tree, + GtkTreeRBNode **node) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + if (priv->cursor_node == NULL) + return FALSE; + + *tree = priv->cursor_tree; + *node = priv->cursor_node; + + return TRUE; +} + +GtkTreeViewColumn * +_gtk_tree_view_get_focus_column (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + return priv->focus_column; +} + +void +_gtk_tree_view_set_focus_column (GtkTreeView *tree_view, + GtkTreeViewColumn *column) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreeViewColumn *old_column = priv->focus_column; + + if (old_column == column) + return; + + priv->focus_column = column; +} + +/* x and y are the mouse position + */ +static void +gtk_tree_view_snapshot_arrow (GtkTreeView *tree_view, + GtkSnapshot *snapshot, + GtkTreeRBTree *tree, + GtkTreeRBNode *node) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GdkRectangle area; + GtkStateFlags state = 0; + GtkStyleContext *context; + GtkWidget *widget; + int x_offset = 0; + int x2; + GtkCellRendererState flags = 0; + + widget = GTK_WIDGET (tree_view); + context = gtk_widget_get_style_context (widget); + + if (! GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_PARENT)) + return; + + gtk_tree_view_get_arrow_xrange (tree_view, tree, &x_offset, &x2); + + area.x = x_offset; + area.y = gtk_tree_view_get_cell_area_y_offset (tree_view, tree, node); + area.width = x2 - x_offset; + area.height = gtk_tree_view_get_cell_area_height (tree_view, node); + + if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED)) + flags |= GTK_CELL_RENDERER_SELECTED; + + if (node == priv->prelight_node && + priv->arrow_prelit) + flags |= GTK_CELL_RENDERER_PRELIT; + + state = gtk_cell_renderer_get_state (NULL, widget, flags); + + if (node->children != NULL) + state |= GTK_STATE_FLAG_CHECKED; + else + state &= ~(GTK_STATE_FLAG_CHECKED); + + gtk_style_context_save (context); + + gtk_style_context_set_state (context, state); + gtk_style_context_add_class (context, "expander"); + + gtk_snapshot_save (snapshot); + gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (area.x, area.y)); + gtk_css_style_snapshot_icon (gtk_style_context_lookup_style (context), snapshot, + area.width, area.height); + gtk_snapshot_restore (snapshot); + + gtk_style_context_restore (context); +} + +static void +gtk_tree_view_focus_to_cursor (GtkTreeView *tree_view) + +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreePath *cursor_path; + + if ((priv->tree == NULL) || + (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))) + return; + + cursor_path = NULL; + if (priv->cursor_node) + cursor_path = _gtk_tree_path_new_from_rbtree (priv->cursor_tree, + priv->cursor_node); + + if (cursor_path == NULL) + { + /* Consult the selection before defaulting to the + * first focusable element + */ + GList *selected_rows; + GtkTreeModel *model; + GtkTreeSelection *selection; + + selection = gtk_tree_view_get_selection (tree_view); + selected_rows = gtk_tree_selection_get_selected_rows (selection, &model); + + if (selected_rows) + { + cursor_path = gtk_tree_path_copy((const GtkTreePath *)(selected_rows->data)); + g_list_free_full (selected_rows, (GDestroyNotify) gtk_tree_path_free); + } + else + { + cursor_path = gtk_tree_path_new_first (); + search_first_focusable_path (tree_view, &cursor_path, + TRUE, NULL, NULL); + } + + if (cursor_path) + { + if (gtk_tree_selection_get_mode (priv->selection) == GTK_SELECTION_MULTIPLE) + gtk_tree_view_real_set_cursor (tree_view, cursor_path, 0); + else + gtk_tree_view_real_set_cursor (tree_view, cursor_path, CLEAR_AND_SELECT); + } + } + + if (cursor_path) + { + priv->draw_keyfocus = TRUE; + + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + gtk_tree_path_free (cursor_path); + + if (priv->focus_column == NULL) + { + GList *list; + for (list = priv->columns; list; list = list->next) + { + if (gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (list->data))) + { + GtkCellArea *cell_area; + + _gtk_tree_view_set_focus_column (tree_view, GTK_TREE_VIEW_COLUMN (list->data)); + + /* This happens when the treeview initially grabs focus and there + * is no column in focus, here we explicitly focus into the first cell */ + cell_area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (priv->focus_column)); + if (!gtk_cell_area_get_focus_cell (cell_area)) + { + gboolean rtl; + + rtl = (_gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); + gtk_cell_area_focus (cell_area, + rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT); + } + + break; + } + } + } + } +} + +static void +gtk_tree_view_move_cursor_up_down (GtkTreeView *tree_view, + int count) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + int selection_count; + GtkTreeRBTree *new_cursor_tree = NULL; + GtkTreeRBNode *new_cursor_node = NULL; + GtkTreePath *cursor_path = NULL; + gboolean selectable; + GtkDirectionType direction; + GtkCellArea *cell_area = NULL; + GtkCellRenderer *last_focus_cell = NULL; + GtkTreeIter iter; + + if (priv->cursor_node == NULL) + return; + + cursor_path = _gtk_tree_path_new_from_rbtree (priv->cursor_tree, + priv->cursor_node); + + direction = count < 0 ? GTK_DIR_UP : GTK_DIR_DOWN; + + if (priv->focus_column) + cell_area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (priv->focus_column)); + + /* If focus stays in the area for this row, then just return for this round */ + if (cell_area && (count == -1 || count == 1) && + gtk_tree_model_get_iter (priv->model, &iter, cursor_path)) + { + gtk_tree_view_column_cell_set_cell_data (priv->focus_column, + priv->model, + &iter, + GTK_TREE_RBNODE_FLAG_SET (priv->cursor_node, GTK_TREE_RBNODE_IS_PARENT), + priv->cursor_node->children ? TRUE : FALSE); + + /* Save the last cell that had focus, if we hit the end of the view we'll give + * focus back to it. */ + last_focus_cell = gtk_cell_area_get_focus_cell (cell_area); + + /* If focus stays in the area, no need to change the cursor row */ + if (gtk_cell_area_focus (cell_area, direction)) + return; + } + + selection_count = gtk_tree_selection_count_selected_rows (priv->selection); + selectable = _gtk_tree_selection_row_is_selectable (priv->selection, + priv->cursor_node, + cursor_path); + + if (selection_count == 0 + && gtk_tree_selection_get_mode (priv->selection) != GTK_SELECTION_NONE + && !priv->modify_selection_pressed + && selectable) + { + /* Don't move the cursor, but just select the current node */ + new_cursor_tree = priv->cursor_tree; + new_cursor_node = priv->cursor_node; + } + else + { + if (count == -1) + gtk_tree_rbtree_prev_full (priv->cursor_tree, priv->cursor_node, + &new_cursor_tree, &new_cursor_node); + else + gtk_tree_rbtree_next_full (priv->cursor_tree, priv->cursor_node, + &new_cursor_tree, &new_cursor_node); + } + + gtk_tree_path_free (cursor_path); + + if (new_cursor_node) + { + cursor_path = _gtk_tree_path_new_from_rbtree (new_cursor_tree, new_cursor_node); + + search_first_focusable_path (tree_view, &cursor_path, + (count != -1), + &new_cursor_tree, + &new_cursor_node); + + if (cursor_path) + gtk_tree_path_free (cursor_path); + } + + /* + * If the list has only one item and multi-selection is set then select + * the row (if not yet selected). + */ + if (gtk_tree_selection_get_mode (priv->selection) == GTK_SELECTION_MULTIPLE && + new_cursor_node == NULL) + { + if (count == -1) + gtk_tree_rbtree_next_full (priv->cursor_tree, priv->cursor_node, + &new_cursor_tree, &new_cursor_node); + else + gtk_tree_rbtree_prev_full (priv->cursor_tree, priv->cursor_node, + &new_cursor_tree, &new_cursor_node); + + if (new_cursor_node == NULL + && !GTK_TREE_RBNODE_FLAG_SET (priv->cursor_node, GTK_TREE_RBNODE_IS_SELECTED)) + { + new_cursor_node = priv->cursor_node; + new_cursor_tree = priv->cursor_tree; + } + else + { + new_cursor_tree = NULL; + new_cursor_node = NULL; + } + } + + if (new_cursor_node) + { + cursor_path = _gtk_tree_path_new_from_rbtree (new_cursor_tree, new_cursor_node); + gtk_tree_view_real_set_cursor (tree_view, cursor_path, CLEAR_AND_SELECT | CLAMP_NODE); + gtk_tree_path_free (cursor_path); + + /* Give focus to the area in the new row */ + if (cell_area) + gtk_cell_area_focus (cell_area, direction); + } + else + { + gtk_tree_view_clamp_node_visible (tree_view, + priv->cursor_tree, + priv->cursor_node); + + if (!priv->extend_selection_pressed) + { + if (! gtk_widget_keynav_failed (GTK_WIDGET (tree_view), + count < 0 ? + GTK_DIR_UP : GTK_DIR_DOWN)) + { + GtkWidget *toplevel = GTK_WIDGET (gtk_widget_get_root (GTK_WIDGET (tree_view))); + + if (toplevel) + gtk_widget_child_focus (toplevel, + count < 0 ? + GTK_DIR_TAB_BACKWARD : + GTK_DIR_TAB_FORWARD); + } + } + else + { + gtk_widget_error_bell (GTK_WIDGET (tree_view)); + } + + if (cell_area) + gtk_cell_area_set_focus_cell (cell_area, last_focus_cell); + } +} + +static void +gtk_tree_view_move_cursor_page_up_down (GtkTreeView *tree_view, + int count) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreePath *old_cursor_path = NULL; + GtkTreePath *cursor_path = NULL; + GtkTreeRBTree *start_cursor_tree = NULL; + GtkTreeRBNode *start_cursor_node = NULL; + GtkTreeRBTree *cursor_tree; + GtkTreeRBNode *cursor_node; + int y; + int window_y; + + if (!gtk_widget_has_focus (GTK_WIDGET (tree_view))) + return; + + if (priv->cursor_node == NULL) + return; + + old_cursor_path = _gtk_tree_path_new_from_rbtree (priv->cursor_tree, + priv->cursor_node); + + y = gtk_tree_rbtree_node_find_offset (priv->cursor_tree, priv->cursor_node); + window_y = RBTREE_Y_TO_TREE_WINDOW_Y (priv, y); + y += priv->cursor_offset; + y += count * (int)gtk_adjustment_get_page_increment (priv->vadjustment); + y = CLAMP (y, (int)gtk_adjustment_get_lower (priv->vadjustment), (int)gtk_adjustment_get_upper (priv->vadjustment)); + + if (y >= gtk_tree_view_get_height (tree_view)) + y = gtk_tree_view_get_height (tree_view) - 1; + + priv->cursor_offset = + gtk_tree_rbtree_find_offset (priv->tree, y, + &cursor_tree, &cursor_node); + + if (cursor_tree == NULL) + { + /* FIXME: we lost the cursor. Should we try to get one? */ + gtk_tree_path_free (old_cursor_path); + return; + } + + if (priv->cursor_offset + > gtk_tree_view_get_row_height (tree_view, cursor_node)) + { + gtk_tree_rbtree_next_full (cursor_tree, cursor_node, + &cursor_tree, &cursor_node); + priv->cursor_offset -= gtk_tree_view_get_row_height (tree_view, cursor_node); + } + + y -= priv->cursor_offset; + cursor_path = _gtk_tree_path_new_from_rbtree (cursor_tree, cursor_node); + + start_cursor_tree = cursor_tree; + start_cursor_node = cursor_node; + + if (! search_first_focusable_path (tree_view, &cursor_path, + (count != -1), + &cursor_tree, &cursor_node)) + { + /* It looks like we reached the end of the view without finding + * a focusable row. We will step backwards to find the last + * focusable row. + */ + cursor_tree = start_cursor_tree; + cursor_node = start_cursor_node; + cursor_path = _gtk_tree_path_new_from_rbtree (cursor_tree, cursor_node); + + search_first_focusable_path (tree_view, &cursor_path, + (count == -1), + &cursor_tree, &cursor_node); + } + + if (!cursor_path) + goto cleanup; + + /* update y */ + y = gtk_tree_rbtree_node_find_offset (cursor_tree, cursor_node); + + gtk_tree_view_real_set_cursor (tree_view, cursor_path, CLEAR_AND_SELECT); + + y -= window_y; + gtk_tree_view_scroll_to_point (tree_view, -1, y); + gtk_tree_view_clamp_node_visible (tree_view, cursor_tree, cursor_node); + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + + if (!gtk_tree_path_compare (old_cursor_path, cursor_path)) + gtk_widget_error_bell (GTK_WIDGET (tree_view)); + + gtk_widget_grab_focus (GTK_WIDGET (tree_view)); + +cleanup: + gtk_tree_path_free (old_cursor_path); + gtk_tree_path_free (cursor_path); +} + +static void +gtk_tree_view_move_cursor_left_right (GtkTreeView *tree_view, + int count) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreePath *cursor_path = NULL; + GtkTreeViewColumn *column; + GtkTreeIter iter; + GList *list; + gboolean found_column = FALSE; + gboolean rtl; + GtkDirectionType direction; + GtkCellArea *cell_area; + GtkCellRenderer *last_focus_cell = NULL; + GtkCellArea *last_focus_area = NULL; + + rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); + + if (!gtk_widget_has_focus (GTK_WIDGET (tree_view))) + return; + + if (priv->cursor_node == NULL) + return; + + cursor_path = _gtk_tree_path_new_from_rbtree (priv->cursor_tree, + priv->cursor_node); + + if (gtk_tree_model_get_iter (priv->model, &iter, cursor_path) == FALSE) + { + gtk_tree_path_free (cursor_path); + return; + } + gtk_tree_path_free (cursor_path); + + list = rtl ? g_list_last (priv->columns) : g_list_first (priv->columns); + if (priv->focus_column) + { + /* Save the cell/area we are moving focus from, if moving the cursor + * by one step hits the end we'll set focus back here */ + last_focus_area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (priv->focus_column)); + last_focus_cell = gtk_cell_area_get_focus_cell (last_focus_area); + + for (; list; list = (rtl ? list->prev : list->next)) + { + if (list->data == priv->focus_column) + break; + } + } + + direction = count > 0 ? GTK_DIR_RIGHT : GTK_DIR_LEFT; + + while (list) + { + column = list->data; + if (gtk_tree_view_column_get_visible (column) == FALSE) + goto loop_end; + + gtk_tree_view_column_cell_set_cell_data (column, + priv->model, + &iter, + GTK_TREE_RBNODE_FLAG_SET (priv->cursor_node, GTK_TREE_RBNODE_IS_PARENT), + priv->cursor_node->children ? TRUE : FALSE); + + cell_area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (column)); + if (gtk_cell_area_focus (cell_area, direction)) + { + _gtk_tree_view_set_focus_column (tree_view, column); + found_column = TRUE; + break; + } + + loop_end: + if (count == 1) + list = rtl ? list->prev : list->next; + else + list = rtl ? list->next : list->prev; + } + + if (found_column) + { + if (!gtk_tree_view_has_can_focus_cell (tree_view)) + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0); + gtk_widget_grab_focus (GTK_WIDGET (tree_view)); + } + else + { + gtk_widget_error_bell (GTK_WIDGET (tree_view)); + + if (last_focus_area) + gtk_cell_area_set_focus_cell (last_focus_area, last_focus_cell); + } + + gtk_tree_view_clamp_column_visible (tree_view, + priv->focus_column, TRUE); +} + +static void +gtk_tree_view_move_cursor_start_end (GtkTreeView *tree_view, + int count) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreeRBTree *cursor_tree; + GtkTreeRBNode *cursor_node; + GtkTreePath *path; + GtkTreePath *old_path; + + if (!gtk_widget_has_focus (GTK_WIDGET (tree_view))) + return; + + g_return_if_fail (priv->tree != NULL); + + gtk_tree_view_get_cursor (tree_view, &old_path, NULL); + + cursor_tree = priv->tree; + + if (count == -1) + { + cursor_node = gtk_tree_rbtree_first (cursor_tree); + + /* Now go forward to find the first focusable row. */ + path = _gtk_tree_path_new_from_rbtree (cursor_tree, cursor_node); + search_first_focusable_path (tree_view, &path, + TRUE, &cursor_tree, &cursor_node); + } + else + { + cursor_node = cursor_tree->root; + + do + { + while (cursor_node && !gtk_tree_rbtree_is_nil (cursor_node->right)) + cursor_node = cursor_node->right; + if (cursor_node->children == NULL) + break; + + cursor_tree = cursor_node->children; + cursor_node = cursor_tree->root; + } + while (1); + + /* Now go backwards to find last focusable row. */ + path = _gtk_tree_path_new_from_rbtree (cursor_tree, cursor_node); + search_first_focusable_path (tree_view, &path, + FALSE, &cursor_tree, &cursor_node); + } + + if (!path) + goto cleanup; + + if (gtk_tree_path_compare (old_path, path)) + { + gtk_tree_view_real_set_cursor (tree_view, path, CLEAR_AND_SELECT | CLAMP_NODE); + gtk_widget_grab_focus (GTK_WIDGET (tree_view)); + } + else + { + gtk_widget_error_bell (GTK_WIDGET (tree_view)); + } + +cleanup: + gtk_tree_path_free (old_path); + gtk_tree_path_free (path); +} + +static gboolean +gtk_tree_view_real_select_all (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + if (!gtk_widget_has_focus (GTK_WIDGET (tree_view))) + return FALSE; + + if (gtk_tree_selection_get_mode (priv->selection) != GTK_SELECTION_MULTIPLE) + return FALSE; + + gtk_tree_selection_select_all (priv->selection); + + return TRUE; +} + +static gboolean +gtk_tree_view_real_unselect_all (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + if (!gtk_widget_has_focus (GTK_WIDGET (tree_view))) + return FALSE; + + if (gtk_tree_selection_get_mode (priv->selection) != GTK_SELECTION_MULTIPLE) + return FALSE; + + gtk_tree_selection_unselect_all (priv->selection); + + return TRUE; +} + +static gboolean +gtk_tree_view_real_select_cursor_row (GtkTreeView *tree_view, + gboolean start_editing) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreeRBTree *new_tree = NULL; + GtkTreeRBNode *new_node = NULL; + GtkTreeRBTree *cursor_tree = NULL; + GtkTreeRBNode *cursor_node = NULL; + GtkTreePath *cursor_path = NULL; + GtkTreeSelectMode mode = 0; + + if (!gtk_widget_has_focus (GTK_WIDGET (tree_view))) + return FALSE; + + if (priv->cursor_node == NULL) + return FALSE; + + cursor_path = _gtk_tree_path_new_from_rbtree (priv->cursor_tree, + priv->cursor_node); + + _gtk_tree_view_find_node (tree_view, cursor_path, + &cursor_tree, &cursor_node); + + if (cursor_tree == NULL) + { + gtk_tree_path_free (cursor_path); + return FALSE; + } + + if (!priv->extend_selection_pressed && start_editing && + priv->focus_column) + { + if (gtk_tree_view_start_editing (tree_view, cursor_path, FALSE)) + { + gtk_tree_path_free (cursor_path); + return TRUE; + } + } + + if (priv->modify_selection_pressed) + mode |= GTK_TREE_SELECT_MODE_TOGGLE; + if (priv->extend_selection_pressed) + mode |= GTK_TREE_SELECT_MODE_EXTEND; + + _gtk_tree_selection_internal_select_node (priv->selection, + cursor_node, + cursor_tree, + cursor_path, + mode, + FALSE); + + /* We bail out if the original (tree, node) don't exist anymore after + * handling the selection-changed callback. We do return TRUE because + * the key press has been handled at this point. + */ + _gtk_tree_view_find_node (tree_view, cursor_path, &new_tree, &new_node); + + if (cursor_tree != new_tree || cursor_node != new_node) + return FALSE; + + gtk_tree_view_clamp_node_visible (tree_view, cursor_tree, cursor_node); + + gtk_widget_grab_focus (GTK_WIDGET (tree_view)); + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + + if (!priv->extend_selection_pressed) + gtk_tree_view_row_activated (tree_view, cursor_path, + priv->focus_column); + + gtk_tree_path_free (cursor_path); + + return TRUE; +} + +static gboolean +gtk_tree_view_real_toggle_cursor_row (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreeRBTree *new_tree = NULL; + GtkTreeRBNode *new_node = NULL; + GtkTreePath *cursor_path = NULL; + + if (!gtk_widget_has_focus (GTK_WIDGET (tree_view))) + return FALSE; + + if (priv->cursor_node == NULL) + return FALSE; + + cursor_path = _gtk_tree_path_new_from_rbtree (priv->cursor_tree, + priv->cursor_node); + + _gtk_tree_selection_internal_select_node (priv->selection, + priv->cursor_node, + priv->cursor_tree, + cursor_path, + GTK_TREE_SELECT_MODE_TOGGLE, + FALSE); + + /* We bail out if the original (tree, node) don't exist anymore after + * handling the selection-changed callback. We do return TRUE because + * the key press has been handled at this point. + */ + _gtk_tree_view_find_node (tree_view, cursor_path, &new_tree, &new_node); + + if (priv->cursor_node != new_node) + return FALSE; + + gtk_tree_view_clamp_node_visible (tree_view, + priv->cursor_tree, + priv->cursor_node); + + gtk_widget_grab_focus (GTK_WIDGET (tree_view)); + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + gtk_tree_path_free (cursor_path); + + return TRUE; +} + +static gboolean +gtk_tree_view_real_expand_collapse_cursor_row (GtkTreeView *tree_view, + gboolean logical, + gboolean expand, + gboolean open_all) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreePath *cursor_path = NULL; + + if (!gtk_widget_has_focus (GTK_WIDGET (tree_view))) + return FALSE; + + if (priv->cursor_node == NULL) + return FALSE; + + cursor_path = _gtk_tree_path_new_from_rbtree (priv->cursor_tree, + priv->cursor_node); + + /* Don't handle the event if we aren't an expander */ + if (!GTK_TREE_RBNODE_FLAG_SET (priv->cursor_node, GTK_TREE_RBNODE_IS_PARENT)) + return FALSE; + + if (!logical + && gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL) + expand = !expand; + + if (expand) + gtk_tree_view_real_expand_row (tree_view, + cursor_path, + priv->cursor_tree, + priv->cursor_node, + open_all); + else + gtk_tree_view_real_collapse_row (tree_view, + cursor_path, + priv->cursor_tree, + priv->cursor_node); + + gtk_tree_path_free (cursor_path); + + return TRUE; +} + +static gboolean +gtk_tree_view_real_select_cursor_parent (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreePath *cursor_path = NULL; + + if (!gtk_widget_has_focus (GTK_WIDGET (tree_view))) + goto out; + + if (priv->cursor_node == NULL) + goto out; + + cursor_path = _gtk_tree_path_new_from_rbtree (priv->cursor_tree, + priv->cursor_node); + + if (priv->cursor_tree->parent_node) + { + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + + gtk_tree_path_up (cursor_path); + + gtk_tree_view_real_set_cursor (tree_view, cursor_path, CLEAR_AND_SELECT | CLAMP_NODE); + gtk_tree_path_free (cursor_path); + + gtk_widget_grab_focus (GTK_WIDGET (tree_view)); + + return TRUE; + } + + out: + + priv->search_entry_avoid_unhandled_binding = TRUE; + return FALSE; +} + +static gboolean +gtk_tree_view_search_entry_flush_timeout (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + gtk_tree_view_search_popover_hide (priv->search_popover, tree_view); + priv->typeselect_flush_timeout = 0; + + return FALSE; +} + +static void +gtk_tree_view_ensure_interactive_directory (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkEventController *controller; + GtkGesture *gesture; + + if (priv->search_custom_entry_set) + return; + + if (priv->search_popover) + return; + + priv->search_popover = gtk_popover_new (); + gtk_css_node_insert_after (gtk_widget_get_css_node (GTK_WIDGET (tree_view)), + gtk_widget_get_css_node (priv->search_popover), + priv->header_node); + gtk_widget_set_parent (priv->search_popover, GTK_WIDGET (tree_view)); + gtk_popover_set_autohide (GTK_POPOVER (priv->search_popover), FALSE); + + controller = gtk_event_controller_key_new (); + g_signal_connect (controller, "key-pressed", + G_CALLBACK (gtk_tree_view_search_key_pressed), + tree_view); + gtk_widget_add_controller (priv->search_popover, controller); + + gesture = gtk_gesture_click_new (); + g_signal_connect (gesture, "pressed", + G_CALLBACK (gtk_tree_view_search_pressed_cb), tree_view); + gtk_widget_add_controller (priv->search_popover, GTK_EVENT_CONTROLLER (gesture)); + + controller = gtk_event_controller_scroll_new (GTK_EVENT_CONTROLLER_SCROLL_VERTICAL); + g_signal_connect (controller, "scroll", + G_CALLBACK (gtk_tree_view_search_scroll_event), + tree_view); + gtk_widget_add_controller (priv->search_popover, controller); + + priv->search_entry = gtk_text_new (); + + controller = gtk_text_get_key_controller (GTK_TEXT (priv->search_entry)); + gtk_event_controller_set_propagation_limit (controller, GTK_LIMIT_NONE); + + g_signal_connect (priv->search_entry, "activate", + G_CALLBACK (gtk_tree_view_search_activate), tree_view); + g_signal_connect (priv->search_entry, "preedit-changed", + G_CALLBACK (gtk_tree_view_search_preedit_changed), tree_view); + g_signal_connect (priv->search_entry, "changed", + G_CALLBACK (gtk_tree_view_search_changed), tree_view); + + gtk_popover_set_child (GTK_POPOVER (priv->search_popover), priv->search_entry); + + gtk_widget_realize (priv->search_entry); +} + +/* Pops up the interactive search entry. If keybinding is TRUE then the user + * started this by typing the start_interactive_search keybinding. Otherwise, it came from + */ +static gboolean +gtk_tree_view_real_start_interactive_search (GtkTreeView *tree_view, + gboolean keybinding) +{ + /* We only start interactive search if we have focus or the columns + * have focus. If one of our children have focus, we don't want to + * start the search. + */ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GList *list; + gboolean found_focus = FALSE; + + if (!priv->enable_search && !keybinding) + return FALSE; + + if (priv->search_custom_entry_set) + return FALSE; + + if (priv->search_popover && + gtk_widget_get_visible (priv->search_popover)) + return TRUE; + + for (list = priv->columns; list; list = list->next) + { + GtkTreeViewColumn *column; + GtkWidget *button; + + column = list->data; + if (!gtk_tree_view_column_get_visible (column)) + continue; + + button = gtk_tree_view_column_get_button (column); + if (gtk_widget_has_focus (button)) + { + found_focus = TRUE; + break; + } + } + + if (gtk_widget_has_focus (GTK_WIDGET (tree_view))) + found_focus = TRUE; + + if (!found_focus) + return FALSE; + + if (priv->search_column < 0) + return FALSE; + + gtk_tree_view_ensure_interactive_directory (tree_view); + + if (keybinding) + gtk_editable_set_text (GTK_EDITABLE (priv->search_entry), ""); + + /* Grab focus without selecting all the text. */ + gtk_text_grab_focus_without_selecting (GTK_TEXT (priv->search_entry)); + + gtk_popover_popup (GTK_POPOVER (priv->search_popover)); + if (priv->search_entry_changed_id == 0) + { + priv->search_entry_changed_id = + g_signal_connect (priv->search_entry, "changed", + G_CALLBACK (gtk_tree_view_search_init), + tree_view); + } + + priv->typeselect_flush_timeout = + g_timeout_add (GTK_TREE_VIEW_SEARCH_DIALOG_TIMEOUT, + (GSourceFunc) gtk_tree_view_search_entry_flush_timeout, + tree_view); + gdk_source_set_static_name_by_id (priv->typeselect_flush_timeout, "[gtk] gtk_tree_view_search_entry_flush_timeout"); + + /* search first matching iter */ + gtk_tree_view_search_init (priv->search_entry, tree_view); + + return TRUE; +} + +static gboolean +gtk_tree_view_start_interactive_search (GtkTreeView *tree_view) +{ + return gtk_tree_view_real_start_interactive_search (tree_view, TRUE); +} + +/* Callbacks */ +static void +gtk_tree_view_adjustment_changed (GtkAdjustment *adjustment, + GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + if (gtk_widget_get_realized (GTK_WIDGET (tree_view))) + { + GtkAllocation allocation; + int dy; + + gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation); + dy = priv->dy - (int) gtk_adjustment_get_value (priv->vadjustment); + + if (dy != 0) + { + /* update our dy and top_row */ + priv->dy = (int) gtk_adjustment_get_value (priv->vadjustment); + + update_prelight (tree_view, + priv->event_last_x, + priv->event_last_y); + + if (!priv->in_top_row_to_dy) + gtk_tree_view_dy_to_top_row (tree_view); + + } + } + + gtk_widget_queue_allocate (GTK_WIDGET (tree_view)); +} + + + +/* Public methods + */ + +/** + * gtk_tree_view_new: + * + * Creates a new `GtkTreeView` widget. + * + * Returns: A newly created `GtkTreeView` widget. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +GtkWidget * +gtk_tree_view_new (void) +{ + return g_object_new (GTK_TYPE_TREE_VIEW, NULL); +} + +/** + * gtk_tree_view_new_with_model: + * @model: the model. + * + * Creates a new `GtkTreeView` widget with the model initialized to @model. + * + * Returns: A newly created `GtkTreeView` widget. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +GtkWidget * +gtk_tree_view_new_with_model (GtkTreeModel *model) +{ + return g_object_new (GTK_TYPE_TREE_VIEW, "model", model, NULL); +} + +/* Public Accessors + */ + +/** + * gtk_tree_view_get_model: + * @tree_view: a `GtkTreeView` + * + * Returns the model the `GtkTreeView` is based on. Returns %NULL if the + * model is unset. + * + * Returns: (transfer none) (nullable): A `GtkTreeModel` + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +GtkTreeModel * +gtk_tree_view_get_model (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL); + + return priv->model; +} + +/** + * gtk_tree_view_set_model: + * @tree_view: A `GtkTreeView`. + * @model: (nullable): The model. + * + * Sets the model for a `GtkTreeView`. If the @tree_view already has a model + * set, it will remove it before setting the new model. If @model is %NULL, + * then it will unset the old model. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +void +gtk_tree_view_set_model (GtkTreeView *tree_view, + GtkTreeModel *model) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model)); + + if (model == priv->model) + return; + + if (priv->scroll_to_path) + { + gtk_tree_row_reference_free (priv->scroll_to_path); + priv->scroll_to_path = NULL; + } + + if (priv->rubber_band_status) + gtk_tree_view_stop_rubber_band (tree_view); + + if (priv->model) + { + GList *tmplist = priv->columns; + + gtk_tree_view_unref_and_check_selection_tree (tree_view, priv->tree); + gtk_tree_view_stop_editing (tree_view, TRUE); + + g_signal_handlers_disconnect_by_func (priv->model, + gtk_tree_view_row_changed, + tree_view); + g_signal_handlers_disconnect_by_func (priv->model, + gtk_tree_view_row_inserted, + tree_view); + g_signal_handlers_disconnect_by_func (priv->model, + gtk_tree_view_row_has_child_toggled, + tree_view); + g_signal_handlers_disconnect_by_func (priv->model, + gtk_tree_view_row_deleted, + tree_view); + g_signal_handlers_disconnect_by_func (priv->model, + gtk_tree_view_rows_reordered, + tree_view); + + for (; tmplist; tmplist = tmplist->next) + _gtk_tree_view_column_unset_model (tmplist->data, + priv->model); + + if (priv->tree) + gtk_tree_view_free_rbtree (tree_view); + + gtk_tree_row_reference_free (priv->drag_dest_row); + priv->drag_dest_row = NULL; + gtk_tree_row_reference_free (priv->anchor); + priv->anchor = NULL; + gtk_tree_row_reference_free (priv->top_row); + priv->top_row = NULL; + gtk_tree_row_reference_free (priv->scroll_to_path); + priv->scroll_to_path = NULL; + + priv->scroll_to_column = NULL; + + g_object_unref (priv->model); + + priv->search_column = -1; + priv->fixed_height_check = 0; + priv->fixed_height = -1; + priv->dy = priv->top_row_dy = 0; + } + + priv->model = model; + + if (priv->model) + { + int i; + GtkTreePath *path; + GtkTreeIter iter; + GtkTreeModelFlags flags; + + if (priv->search_column == -1) + { + for (i = 0; i < gtk_tree_model_get_n_columns (model); i++) + { + GType type = gtk_tree_model_get_column_type (model, i); + + if (g_value_type_transformable (type, G_TYPE_STRING)) + { + priv->search_column = i; + break; + } + } + } + + g_object_ref (priv->model); + g_signal_connect (priv->model, + "row-changed", + G_CALLBACK (gtk_tree_view_row_changed), + tree_view); + g_signal_connect (priv->model, + "row-inserted", + G_CALLBACK (gtk_tree_view_row_inserted), + tree_view); + g_signal_connect (priv->model, + "row-has-child-toggled", + G_CALLBACK (gtk_tree_view_row_has_child_toggled), + tree_view); + g_signal_connect (priv->model, + "row-deleted", + G_CALLBACK (gtk_tree_view_row_deleted), + tree_view); + g_signal_connect (priv->model, + "rows-reordered", + G_CALLBACK (gtk_tree_view_rows_reordered), + tree_view); + + flags = gtk_tree_model_get_flags (priv->model); + if ((flags & GTK_TREE_MODEL_LIST_ONLY) == GTK_TREE_MODEL_LIST_ONLY) + priv->is_list = TRUE; + else + priv->is_list = FALSE; + + path = gtk_tree_path_new_first (); + if (gtk_tree_model_get_iter (priv->model, &iter, path)) + { + priv->tree = gtk_tree_rbtree_new (); + gtk_tree_view_build_tree (tree_view, priv->tree, &iter, 1, FALSE); + } + gtk_tree_path_free (path); + + /* FIXME: do I need to do this? gtk_tree_view_create_buttons (tree_view); */ + install_presize_handler (tree_view); + } + + gtk_tree_view_real_set_cursor (tree_view, NULL, CURSOR_INVALID); + + g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_MODEL]); + + if (priv->selection) + _gtk_tree_selection_emit_changed (priv->selection); + + if (gtk_widget_get_realized (GTK_WIDGET (tree_view))) + gtk_widget_queue_resize (GTK_WIDGET (tree_view)); +} + +/** + * gtk_tree_view_get_selection: + * @tree_view: A `GtkTreeView`. + * + * Gets the `GtkTreeSelection` associated with @tree_view. + * + * Returns: (transfer none): A `GtkTreeSelection` object. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +GtkTreeSelection * +gtk_tree_view_get_selection (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL); + + return priv->selection; +} + +static void +gtk_tree_view_do_set_hadjustment (GtkTreeView *tree_view, + GtkAdjustment *adjustment) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + if (adjustment && priv->hadjustment == adjustment) + return; + + if (priv->hadjustment != NULL) + { + g_signal_handlers_disconnect_by_func (priv->hadjustment, + gtk_tree_view_adjustment_changed, + tree_view); + g_object_unref (priv->hadjustment); + } + + if (adjustment == NULL) + adjustment = gtk_adjustment_new (0.0, 0.0, 0.0, + 0.0, 0.0, 0.0); + + g_signal_connect (adjustment, "value-changed", + G_CALLBACK (gtk_tree_view_adjustment_changed), tree_view); + priv->hadjustment = g_object_ref_sink (adjustment); + /* FIXME: Adjustment should probably be populated here with fresh values, but + * internal details are too complicated for me to decipher right now. + */ + gtk_tree_view_adjustment_changed (NULL, tree_view); + + g_object_notify (G_OBJECT (tree_view), "hadjustment"); +} + +static void +gtk_tree_view_do_set_vadjustment (GtkTreeView *tree_view, + GtkAdjustment *adjustment) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + if (adjustment && priv->vadjustment == adjustment) + return; + + if (priv->vadjustment != NULL) + { + g_signal_handlers_disconnect_by_func (priv->vadjustment, + gtk_tree_view_adjustment_changed, + tree_view); + g_object_unref (priv->vadjustment); + } + + if (adjustment == NULL) + adjustment = gtk_adjustment_new (0.0, 0.0, 0.0, + 0.0, 0.0, 0.0); + + g_signal_connect (adjustment, "value-changed", + G_CALLBACK (gtk_tree_view_adjustment_changed), tree_view); + priv->vadjustment = g_object_ref_sink (adjustment); + /* FIXME: Adjustment should probably be populated here with fresh values, but + * internal details are too complicated for me to decipher right now. + */ + gtk_tree_view_adjustment_changed (NULL, tree_view); + g_object_notify (G_OBJECT (tree_view), "vadjustment"); +} + +/* Column and header operations */ + +/** + * gtk_tree_view_get_headers_visible: + * @tree_view: A `GtkTreeView`. + * + * Returns %TRUE if the headers on the @tree_view are visible. + * + * Returns: Whether the headers are visible or not. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +gboolean +gtk_tree_view_get_headers_visible (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); + + return priv->headers_visible; +} + +/** + * gtk_tree_view_set_headers_visible: + * @tree_view: A `GtkTreeView`. + * @headers_visible: %TRUE if the headers are visible + * + * Sets the visibility state of the headers. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +void +gtk_tree_view_set_headers_visible (GtkTreeView *tree_view, + gboolean headers_visible) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GList *list; + GtkTreeViewColumn *column; + GtkWidget *button; + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + headers_visible = !! headers_visible; + + if (priv->headers_visible == headers_visible) + return; + + priv->headers_visible = headers_visible == TRUE; + + if (gtk_widget_get_realized (GTK_WIDGET (tree_view))) + { + if (headers_visible) + { + if (gtk_widget_get_mapped (GTK_WIDGET (tree_view))) + gtk_tree_view_map_buttons (tree_view); + } + else + { + + for (list = priv->columns; list; list = list->next) + { + column = list->data; + button = gtk_tree_view_column_get_button (column); + + gtk_widget_hide (button); + gtk_widget_unmap (button); + } + } + } + + gtk_widget_queue_resize (GTK_WIDGET (tree_view)); + + g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_HEADERS_VISIBLE]); +} + +/** + * gtk_tree_view_columns_autosize: + * @tree_view: A `GtkTreeView`. + * + * Resizes all columns to their optimal width. Only works after the + * treeview has been realized. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +void +gtk_tree_view_columns_autosize (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + gboolean dirty = FALSE; + GList *list; + GtkTreeViewColumn *column; + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + for (list = priv->columns; list; list = list->next) + { + column = list->data; + if (gtk_tree_view_column_get_sizing (column) == GTK_TREE_VIEW_COLUMN_AUTOSIZE) + continue; + _gtk_tree_view_column_cell_set_dirty (column, TRUE); + dirty = TRUE; + } + + if (dirty) + gtk_widget_queue_resize (GTK_WIDGET (tree_view)); +} + +/** + * gtk_tree_view_set_headers_clickable: + * @tree_view: A `GtkTreeView`. + * @setting: %TRUE if the columns are clickable. + * + * Allow the column title buttons to be clicked. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +void +gtk_tree_view_set_headers_clickable (GtkTreeView *tree_view, + gboolean setting) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GList *list; + gboolean changed = FALSE; + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + for (list = priv->columns; list; list = list->next) + { + if (gtk_tree_view_column_get_clickable (GTK_TREE_VIEW_COLUMN (list->data)) != setting) + { + gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (list->data), setting); + changed = TRUE; + } + } + + if (changed) + g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_HEADERS_CLICKABLE]); +} + + +/** + * gtk_tree_view_get_headers_clickable: + * @tree_view: A `GtkTreeView`. + * + * Returns whether all header columns are clickable. + * + * Returns: %TRUE if all header columns are clickable, otherwise %FALSE + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +gboolean +gtk_tree_view_get_headers_clickable (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GList *list; + + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); + + for (list = priv->columns; list; list = list->next) + if (!gtk_tree_view_column_get_clickable (GTK_TREE_VIEW_COLUMN (list->data))) + return FALSE; + + return TRUE; +} + +/** + * gtk_tree_view_set_activate_on_single_click: + * @tree_view: a `GtkTreeView` + * @single: %TRUE to emit row-activated on a single click + * + * Cause the `GtkTreeView`::row-activated signal to be emitted + * on a single click instead of a double click. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +void +gtk_tree_view_set_activate_on_single_click (GtkTreeView *tree_view, + gboolean single) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + single = single != FALSE; + + if (priv->activate_on_single_click == single) + return; + + priv->activate_on_single_click = single; + g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_ACTIVATE_ON_SINGLE_CLICK]); +} + +/** + * gtk_tree_view_get_activate_on_single_click: + * @tree_view: a `GtkTreeView` + * + * Gets the setting set by gtk_tree_view_set_activate_on_single_click(). + * + * Returns: %TRUE if row-activated will be emitted on a single click + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +gboolean +gtk_tree_view_get_activate_on_single_click (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); + + return priv->activate_on_single_click; +} + +/* Public Column functions + */ + +/** + * gtk_tree_view_append_column: + * @tree_view: A `GtkTreeView`. + * @column: The `GtkTreeViewColumn` to add. + * + * Appends @column to the list of columns. If @tree_view has “fixed_height” + * mode enabled, then @column must have its “sizing” property set to be + * GTK_TREE_VIEW_COLUMN_FIXED. + * + * Returns: The number of columns in @tree_view after appending. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +int +gtk_tree_view_append_column (GtkTreeView *tree_view, + GtkTreeViewColumn *column) +{ + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1); + g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (column), -1); + g_return_val_if_fail (gtk_tree_view_column_get_tree_view (column) == NULL, -1); + + return gtk_tree_view_insert_column (tree_view, column, -1); +} + +/** + * gtk_tree_view_remove_column: + * @tree_view: A `GtkTreeView`. + * @column: The `GtkTreeViewColumn` to remove. + * + * Removes @column from @tree_view. + * + * Returns: The number of columns in @tree_view after removing. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +int +gtk_tree_view_remove_column (GtkTreeView *tree_view, + GtkTreeViewColumn *column) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1); + g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (column), -1); + g_return_val_if_fail (gtk_tree_view_column_get_tree_view (column) == GTK_WIDGET (tree_view), -1); + + if (priv->focus_column == column) + _gtk_tree_view_set_focus_column (tree_view, NULL); + + if (priv->edited_column == column) + { + gtk_tree_view_stop_editing (tree_view, TRUE); + + /* no need to, but just to be sure ... */ + priv->edited_column = NULL; + } + + if (priv->expander_column == column) + priv->expander_column = NULL; + + g_signal_handlers_disconnect_by_func (column, + G_CALLBACK (column_sizing_notify), + tree_view); + + _gtk_tree_view_column_unset_tree_view (column); + + priv->columns = g_list_remove (priv->columns, column); + priv->n_columns--; + + if (gtk_widget_get_realized (GTK_WIDGET (tree_view))) + { + GList *list; + + for (list = priv->columns; list; list = list->next) + { + GtkTreeViewColumn *tmp_column; + + tmp_column = GTK_TREE_VIEW_COLUMN (list->data); + if (gtk_tree_view_column_get_visible (tmp_column)) + _gtk_tree_view_column_cell_set_dirty (tmp_column, TRUE); + } + + gtk_widget_queue_resize (GTK_WIDGET (tree_view)); + } + + g_object_unref (column); + g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0); + + return priv->n_columns; +} + +/** + * gtk_tree_view_insert_column: + * @tree_view: A `GtkTreeView`. + * @column: The `GtkTreeViewColumn` to be inserted. + * @position: The position to insert @column in. + * + * This inserts the @column into the @tree_view at @position. If @position is + * -1, then the column is inserted at the end. If @tree_view has + * “fixed_height” mode enabled, then @column must have its “sizing” property + * set to be GTK_TREE_VIEW_COLUMN_FIXED. + * + * Returns: The number of columns in @tree_view after insertion. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +int +gtk_tree_view_insert_column (GtkTreeView *tree_view, + GtkTreeViewColumn *column, + int position) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1); + g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (column), -1); + g_return_val_if_fail (gtk_tree_view_column_get_tree_view (column) == NULL, -1); + + if (priv->fixed_height_mode) + g_return_val_if_fail (gtk_tree_view_column_get_sizing (column) + == GTK_TREE_VIEW_COLUMN_FIXED, -1); + + if (position < 0 || position > priv->n_columns) + position = priv->n_columns; + + g_object_ref_sink (column); + + g_signal_connect (column, "notify::sizing", + G_CALLBACK (column_sizing_notify), tree_view); + + priv->columns = g_list_insert (priv->columns, + column, position); + priv->n_columns++; + + _gtk_tree_view_column_set_tree_view (column, tree_view); + + /* XXX: We need to reparent the node into the header, somebody make that a real widget */ + gtk_css_node_set_parent (gtk_widget_get_css_node (gtk_tree_view_column_get_button (column)), NULL); + gtk_tree_view_update_button_position (tree_view, column); + + if (gtk_widget_get_realized (GTK_WIDGET (tree_view))) + { + GList *list; + + _gtk_tree_view_column_realize_button (column); + + for (list = priv->columns; list; list = list->next) + { + column = GTK_TREE_VIEW_COLUMN (list->data); + if (gtk_tree_view_column_get_visible (column)) + _gtk_tree_view_column_cell_set_dirty (column, TRUE); + } + gtk_widget_queue_resize (GTK_WIDGET (tree_view)); + } + + g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0); + + return priv->n_columns; +} + +/** + * gtk_tree_view_insert_column_with_attributes: + * @tree_view: A `GtkTreeView` + * @position: The position to insert the new column in + * @title: The title to set the header to + * @cell: The `GtkCellRenderer` + * @...: A %NULL-terminated list of attributes + * + * Creates a new `GtkTreeViewColumn` and inserts it into the @tree_view at + * @position. If @position is -1, then the newly created column is inserted at + * the end. The column is initialized with the attributes given. If @tree_view + * has “fixed_height” mode enabled, then the new column will have its sizing + * property set to be GTK_TREE_VIEW_COLUMN_FIXED. + * + * Returns: The number of columns in @tree_view after insertion. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +int +gtk_tree_view_insert_column_with_attributes (GtkTreeView *tree_view, + int position, + const char *title, + GtkCellRenderer *cell, + ...) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreeViewColumn *column; + char *attribute; + va_list args; + int column_id; + + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1); + + column = gtk_tree_view_column_new (); + if (priv->fixed_height_mode) + gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED); + + gtk_tree_view_column_set_title (column, title); + gtk_tree_view_column_pack_start (column, cell, TRUE); + + va_start (args, cell); + + attribute = va_arg (args, char *); + + while (attribute != NULL) + { + column_id = va_arg (args, int); + gtk_tree_view_column_add_attribute (column, cell, attribute, column_id); + attribute = va_arg (args, char *); + } + + va_end (args); + + return gtk_tree_view_insert_column (tree_view, column, position); +} + +/** + * gtk_tree_view_insert_column_with_data_func: + * @tree_view: a `GtkTreeView` + * @position: Position to insert, -1 for append + * @title: column title + * @cell: cell renderer for column + * @func: function to set attributes of cell renderer + * @data: data for @func + * @dnotify: destroy notifier for @data + * + * Convenience function that inserts a new column into the `GtkTreeView` + * with the given cell renderer and a `GtkTreeCellDataFunc` to set cell renderer + * attributes (normally using data from the model). See also + * gtk_tree_view_column_set_cell_data_func(), gtk_tree_view_column_pack_start(). + * If @tree_view has “fixed_height” mode enabled, then the new column will have its + * “sizing” property set to be GTK_TREE_VIEW_COLUMN_FIXED. + * + * Returns: number of columns in the tree view post-insert + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +int +gtk_tree_view_insert_column_with_data_func (GtkTreeView *tree_view, + int position, + const char *title, + GtkCellRenderer *cell, + GtkTreeCellDataFunc func, + gpointer data, + GDestroyNotify dnotify) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreeViewColumn *column; + + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1); + + column = gtk_tree_view_column_new (); + if (priv->fixed_height_mode) + gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED); + + gtk_tree_view_column_set_title (column, title); + gtk_tree_view_column_pack_start (column, cell, TRUE); + gtk_tree_view_column_set_cell_data_func (column, cell, func, data, dnotify); + + return gtk_tree_view_insert_column (tree_view, column, position); +} + +/** + * gtk_tree_view_get_n_columns: + * @tree_view: a `GtkTreeView` + * + * Queries the number of columns in the given @tree_view. + * + * Returns: The number of columns in the @tree_view + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +guint +gtk_tree_view_get_n_columns (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), 0); + + return priv->n_columns; +} + +/** + * gtk_tree_view_get_column: + * @tree_view: A `GtkTreeView`. + * @n: The position of the column, counting from 0. + * + * Gets the `GtkTreeViewColumn` at the given position in the #tree_view. + * + * Returns: (nullable) (transfer none): The `GtkTreeViewColumn`, or %NULL if the + * position is outside the range of columns. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +GtkTreeViewColumn * +gtk_tree_view_get_column (GtkTreeView *tree_view, + int n) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL); + + if (n < 0 || n >= priv->n_columns) + return NULL; + + if (priv->columns == NULL) + return NULL; + + return GTK_TREE_VIEW_COLUMN (g_list_nth (priv->columns, n)->data); +} + +/** + * gtk_tree_view_get_columns: + * @tree_view: A `GtkTreeView` + * + * Returns a `GList` of all the `GtkTreeViewColumn`s currently in @tree_view. + * The returned list must be freed with g_list_free (). + * + * Returns: (element-type GtkTreeViewColumn) (transfer container): A list of `GtkTreeViewColumn`s + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +GList * +gtk_tree_view_get_columns (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL); + + return g_list_copy (priv->columns); +} + +/** + * gtk_tree_view_move_column_after: + * @tree_view: A `GtkTreeView` + * @column: The `GtkTreeViewColumn` to be moved. + * @base_column: (nullable): The `GtkTreeViewColumn` to be moved relative to + * + * Moves @column to be after to @base_column. If @base_column is %NULL, then + * @column is placed in the first position. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +void +gtk_tree_view_move_column_after (GtkTreeView *tree_view, + GtkTreeViewColumn *column, + GtkTreeViewColumn *base_column) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GList *column_list_el, *base_el = NULL; + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + column_list_el = g_list_find (priv->columns, column); + g_return_if_fail (column_list_el != NULL); + + if (base_column) + { + base_el = g_list_find (priv->columns, base_column); + g_return_if_fail (base_el != NULL); + } + + if (column_list_el->prev == base_el) + return; + + priv->columns = g_list_remove_link (priv->columns, column_list_el); + if (base_el == NULL) + { + column_list_el->prev = NULL; + column_list_el->next = priv->columns; + if (column_list_el->next) + column_list_el->next->prev = column_list_el; + priv->columns = column_list_el; + } + else + { + column_list_el->prev = base_el; + column_list_el->next = base_el->next; + if (column_list_el->next) + column_list_el->next->prev = column_list_el; + base_el->next = column_list_el; + } + + gtk_tree_view_update_button_position (tree_view, column); + + gtk_widget_queue_resize (GTK_WIDGET (tree_view)); + + g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0); +} + +/** + * gtk_tree_view_set_expander_column: + * @tree_view: A `GtkTreeView` + * @column: (nullable): %NULL, or the column to draw the expander arrow at. + * + * Sets the column to draw the expander arrow at. It must be in @tree_view. + * If @column is %NULL, then the expander arrow is always at the first + * visible column. + * + * If you do not want expander arrow to appear in your tree, set the + * expander column to a hidden column. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +void +gtk_tree_view_set_expander_column (GtkTreeView *tree_view, + GtkTreeViewColumn *column) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + g_return_if_fail (column == NULL || GTK_IS_TREE_VIEW_COLUMN (column)); + g_return_if_fail (column == NULL || gtk_tree_view_column_get_tree_view (column) == GTK_WIDGET (tree_view)); + + if (priv->expander_column != column) + { + priv->expander_column = column; + g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_EXPANDER_COLUMN]); + } +} + +/** + * gtk_tree_view_get_expander_column: + * @tree_view: A `GtkTreeView` + * + * Returns the column that is the current expander column, + * or %NULL if none has been set. + * This column has the expander arrow drawn next to it. + * + * Returns: (transfer none) (nullable): The expander column. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +GtkTreeViewColumn * +gtk_tree_view_get_expander_column (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GList *list; + + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL); + + for (list = priv->columns; list; list = list->next) + if (gtk_tree_view_is_expander_column (tree_view, GTK_TREE_VIEW_COLUMN (list->data))) + return (GtkTreeViewColumn *) list->data; + return NULL; +} + + +/** + * gtk_tree_view_set_column_drag_function: + * @tree_view: A `GtkTreeView`. + * @func: (nullable): A function to determine which columns are reorderable + * @user_data: (closure): User data to be passed to @func + * @destroy: (nullable): Destroy notifier for @user_data + * + * Sets a user function for determining where a column may be dropped when + * dragged. This function is called on every column pair in turn at the + * beginning of a column drag to determine where a drop can take place. The + * arguments passed to @func are: the @tree_view, the `GtkTreeViewColumn` being + * dragged, the two `GtkTreeViewColumn`s determining the drop spot, and + * @user_data. If either of the `GtkTreeViewColumn` arguments for the drop spot + * are %NULL, then they indicate an edge. If @func is set to be %NULL, then + * @tree_view reverts to the default behavior of allowing all columns to be + * dropped everywhere. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +void +gtk_tree_view_set_column_drag_function (GtkTreeView *tree_view, + GtkTreeViewColumnDropFunc func, + gpointer user_data, + GDestroyNotify destroy) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + if (priv->column_drop_func_data_destroy) + priv->column_drop_func_data_destroy (priv->column_drop_func_data); + + priv->column_drop_func = func; + priv->column_drop_func_data = user_data; + priv->column_drop_func_data_destroy = destroy; +} + +/** + * gtk_tree_view_scroll_to_point: + * @tree_view: a `GtkTreeView` + * @tree_x: X coordinate of new top-left pixel of visible area, or -1 + * @tree_y: Y coordinate of new top-left pixel of visible area, or -1 + * + * Scrolls the tree view such that the top-left corner of the visible + * area is @tree_x, @tree_y, where @tree_x and @tree_y are specified + * in tree coordinates. The @tree_view must be realized before + * this function is called. If it isn't, you probably want to be + * using gtk_tree_view_scroll_to_cell(). + * + * If either @tree_x or @tree_y are -1, then that direction isn’t scrolled. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +void +gtk_tree_view_scroll_to_point (GtkTreeView *tree_view, + int tree_x, + int tree_y) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkAdjustment *hadj; + GtkAdjustment *vadj; + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view))); + + hadj = priv->hadjustment; + vadj = priv->vadjustment; + + if (tree_x != -1) + gtk_adjustment_animate_to_value (hadj, tree_x); + if (tree_y != -1) + gtk_adjustment_animate_to_value (vadj, tree_y); +} + +/** + * gtk_tree_view_scroll_to_cell: + * @tree_view: A `GtkTreeView`. + * @path: (nullable): The path of the row to move to + * @column: (nullable): The `GtkTreeViewColumn` to move horizontally to + * @use_align: whether to use alignment arguments, or %FALSE. + * @row_align: The vertical alignment of the row specified by @path. + * @col_align: The horizontal alignment of the column specified by @column. + * + * Moves the alignments of @tree_view to the position specified by @column and + * @path. If @column is %NULL, then no horizontal scrolling occurs. Likewise, + * if @path is %NULL no vertical scrolling occurs. At a minimum, one of @column + * or @path need to be non-%NULL. @row_align determines where the row is + * placed, and @col_align determines where @column is placed. Both are expected + * to be between 0.0 and 1.0. 0.0 means left/top alignment, 1.0 means + * right/bottom alignment, 0.5 means center. + * + * If @use_align is %FALSE, then the alignment arguments are ignored, and the + * tree does the minimum amount of work to scroll the cell onto the screen. + * This means that the cell will be scrolled to the edge closest to its current + * position. If the cell is currently visible on the screen, nothing is done. + * + * This function only works if the model is set, and @path is a valid row on the + * model. If the model changes before the @tree_view is realized, the centered + * path will be modified to reflect this change. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +void +gtk_tree_view_scroll_to_cell (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + gboolean use_align, + float row_align, + float col_align) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + g_return_if_fail (priv->model != NULL); + g_return_if_fail (priv->tree != NULL); + g_return_if_fail (row_align >= 0.0 && row_align <= 1.0); + g_return_if_fail (col_align >= 0.0 && col_align <= 1.0); + g_return_if_fail (path != NULL || column != NULL); + + row_align = CLAMP (row_align, 0.0, 1.0); + col_align = CLAMP (col_align, 0.0, 1.0); + + + /* Note: Despite the benefits that come from having one code path for the + * scrolling code, we short-circuit validate_visible_area's immplementation as + * it is much slower than just going to the point. + */ + if (!gtk_widget_get_visible (GTK_WIDGET (tree_view)) || + !gtk_widget_get_realized (GTK_WIDGET (tree_view)) || + _gtk_widget_get_alloc_needed (GTK_WIDGET (tree_view)) || + GTK_TREE_RBNODE_FLAG_SET (priv->tree->root, GTK_TREE_RBNODE_DESCENDANTS_INVALID)) + { + if (priv->scroll_to_path) + gtk_tree_row_reference_free (priv->scroll_to_path); + + priv->scroll_to_path = NULL; + priv->scroll_to_column = NULL; + + if (path) + priv->scroll_to_path = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), priv->model, path); + if (column) + priv->scroll_to_column = column; + priv->scroll_to_use_align = use_align; + priv->scroll_to_row_align = row_align; + priv->scroll_to_col_align = col_align; + + install_presize_handler (tree_view); + } + else + { + GdkRectangle cell_rect; + GdkRectangle vis_rect; + int dest_x, dest_y; + + gtk_tree_view_get_background_area (tree_view, path, column, &cell_rect); + gtk_tree_view_get_visible_rect (tree_view, &vis_rect); + + cell_rect.y = TREE_WINDOW_Y_TO_RBTREE_Y (priv, cell_rect.y); + + dest_x = vis_rect.x; + dest_y = vis_rect.y; + + if (column) + { + if (use_align) + { + dest_x = cell_rect.x - ((vis_rect.width - cell_rect.width) * col_align); + } + else + { + if (cell_rect.x < vis_rect.x) + dest_x = cell_rect.x; + if (cell_rect.x + cell_rect.width > vis_rect.x + vis_rect.width) + dest_x = cell_rect.x + cell_rect.width - vis_rect.width; + } + } + + if (path) + { + if (use_align) + { + dest_y = cell_rect.y - ((vis_rect.height - cell_rect.height) * row_align); + dest_y = MAX (dest_y, 0); + } + else + { + if (cell_rect.y < vis_rect.y) + dest_y = cell_rect.y; + if (cell_rect.y + cell_rect.height > vis_rect.y + vis_rect.height) + dest_y = cell_rect.y + cell_rect.height - vis_rect.height; + } + } + + gtk_tree_view_scroll_to_point (tree_view, dest_x, dest_y); + } +} + +/** + * gtk_tree_view_row_activated: + * @tree_view: A `GtkTreeView` + * @path: The `GtkTreePath` to be activated. + * @column: (nullable): The `GtkTreeViewColumn` to be activated. + * + * Activates the cell determined by @path and @column. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +void +gtk_tree_view_row_activated (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column) +{ + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + g_signal_emit (tree_view, tree_view_signals[ROW_ACTIVATED], 0, path, column); +} + + +static void +gtk_tree_view_expand_all_emission_helper (GtkTreeRBTree *tree, + GtkTreeRBNode *node, + gpointer data) +{ + GtkTreeView *tree_view = data; + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + if ((node->flags & GTK_TREE_RBNODE_IS_PARENT) == GTK_TREE_RBNODE_IS_PARENT && + node->children) + { + GtkTreePath *path; + GtkTreeIter iter; + + path = _gtk_tree_path_new_from_rbtree (tree, node); + gtk_tree_model_get_iter (priv->model, &iter, path); + + g_signal_emit (tree_view, tree_view_signals[ROW_EXPANDED], 0, &iter, path); + + gtk_tree_path_free (path); + } + + if (node->children) + gtk_tree_rbtree_traverse (node->children, + node->children->root, + G_PRE_ORDER, + gtk_tree_view_expand_all_emission_helper, + tree_view); +} + +/** + * gtk_tree_view_expand_all: + * @tree_view: A `GtkTreeView`. + * + * Recursively expands all nodes in the @tree_view. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +void +gtk_tree_view_expand_all (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreePath *path; + GtkTreeRBTree *tree; + GtkTreeRBNode *node; + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + if (priv->tree == NULL) + return; + + path = gtk_tree_path_new_first (); + _gtk_tree_view_find_node (tree_view, path, &tree, &node); + + while (node) + { + gtk_tree_view_real_expand_row (tree_view, path, tree, node, TRUE); + node = gtk_tree_rbtree_next (tree, node); + gtk_tree_path_next (path); + } + + gtk_tree_path_free (path); +} + +/** + * gtk_tree_view_collapse_all: + * @tree_view: A `GtkTreeView`. + * + * Recursively collapses all visible, expanded nodes in @tree_view. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +void +gtk_tree_view_collapse_all (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreeRBTree *tree; + GtkTreeRBNode *node; + GtkTreePath *path; + int *indices; + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + if (priv->tree == NULL) + return; + + path = gtk_tree_path_new (); + gtk_tree_path_down (path); + indices = gtk_tree_path_get_indices (path); + + tree = priv->tree; + node = gtk_tree_rbtree_first (tree); + + while (node) + { + if (node->children) + gtk_tree_view_real_collapse_row (tree_view, path, tree, node); + indices[0]++; + node = gtk_tree_rbtree_next (tree, node); + } + + gtk_tree_path_free (path); +} + +/** + * gtk_tree_view_expand_to_path: + * @tree_view: A `GtkTreeView`. + * @path: path to a row. + * + * Expands the row at @path. This will also expand all parent rows of + * @path as necessary. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +void +gtk_tree_view_expand_to_path (GtkTreeView *tree_view, + GtkTreePath *path) +{ + int i, depth; + int *indices; + GtkTreePath *tmp; + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + g_return_if_fail (path != NULL); + + depth = gtk_tree_path_get_depth (path); + indices = gtk_tree_path_get_indices (path); + + tmp = gtk_tree_path_new (); + g_return_if_fail (tmp != NULL); + + for (i = 0; i < depth; i++) + { + gtk_tree_path_append_index (tmp, indices[i]); + gtk_tree_view_expand_row (tree_view, tmp, FALSE); + } + + gtk_tree_path_free (tmp); +} + +/* FIXME the bool return values for expand_row and collapse_row are + * not analogous; they should be TRUE if the row had children and + * was not already in the requested state. + */ + + +static gboolean +gtk_tree_view_real_expand_row (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeRBTree *tree, + GtkTreeRBNode *node, + gboolean open_all) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreeIter iter; + GtkTreeIter temp; + gboolean expand; + + remove_auto_expand_timeout (tree_view); + + if (node->children && !open_all) + return FALSE; + + if (! GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_PARENT)) + return FALSE; + + gtk_tree_model_get_iter (priv->model, &iter, path); + if (! gtk_tree_model_iter_has_child (priv->model, &iter)) + return FALSE; + + + if (node->children && open_all) + { + gboolean retval = FALSE; + GtkTreePath *tmp_path = gtk_tree_path_copy (path); + + gtk_tree_path_append_index (tmp_path, 0); + tree = node->children; + node = gtk_tree_rbtree_first (tree); + /* try to expand the children */ + do + { + gboolean t; + t = gtk_tree_view_real_expand_row (tree_view, tmp_path, tree, node, + TRUE); + if (t) + retval = TRUE; + + gtk_tree_path_next (tmp_path); + node = gtk_tree_rbtree_next (tree, node); + } + while (node != NULL); + + gtk_tree_path_free (tmp_path); + + return retval; + } + + g_signal_emit (tree_view, tree_view_signals[TEST_EXPAND_ROW], 0, &iter, path, &expand); + + if (!gtk_tree_model_iter_has_child (priv->model, &iter)) + return FALSE; + + if (expand) + return FALSE; + + node->children = gtk_tree_rbtree_new (); + node->children->parent_tree = tree; + node->children->parent_node = node; + + gtk_tree_model_iter_children (priv->model, &temp, &iter); + + gtk_tree_view_build_tree (tree_view, + node->children, + &temp, + gtk_tree_path_get_depth (path) + 1, + open_all); + + install_presize_handler (tree_view); + + g_signal_emit (tree_view, tree_view_signals[ROW_EXPANDED], 0, &iter, path); + if (open_all && node->children) + { + gtk_tree_rbtree_traverse (node->children, + node->children->root, + G_PRE_ORDER, + gtk_tree_view_expand_all_emission_helper, + tree_view); + } + return TRUE; +} + + +/** + * gtk_tree_view_expand_row: + * @tree_view: a `GtkTreeView` + * @path: path to a row + * @open_all: whether to recursively expand, or just expand immediate children + * + * Opens the row so its children are visible. + * + * Returns: %TRUE if the row existed and had children + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +gboolean +gtk_tree_view_expand_row (GtkTreeView *tree_view, + GtkTreePath *path, + gboolean open_all) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreeRBTree *tree; + GtkTreeRBNode *node; + + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); + g_return_val_if_fail (priv->model != NULL, FALSE); + g_return_val_if_fail (path != NULL, FALSE); + + if (_gtk_tree_view_find_node (tree_view, + path, + &tree, + &node)) + return FALSE; + + if (tree != NULL) + return gtk_tree_view_real_expand_row (tree_view, path, tree, node, open_all); + else + return FALSE; +} + +static gboolean +gtk_tree_view_real_collapse_row (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeRBTree *tree, + GtkTreeRBNode *node) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreeIter iter; + GtkTreeIter children; + gboolean collapse; + GList *list; + gboolean selection_changed, cursor_changed; + + remove_auto_expand_timeout (tree_view); + + if (node->children == NULL) + return FALSE; + gtk_tree_model_get_iter (priv->model, &iter, path); + + g_signal_emit (tree_view, tree_view_signals[TEST_COLLAPSE_ROW], 0, &iter, path, &collapse); + + if (collapse) + return FALSE; + + /* if the prelighted node is a child of us, we want to unprelight it. We have + * a chance to prelight the correct node below */ + + if (priv->prelight_tree) + { + GtkTreeRBTree *parent_tree; + GtkTreeRBNode *parent_node; + + parent_tree = priv->prelight_tree->parent_tree; + parent_node = priv->prelight_tree->parent_node; + while (parent_tree) + { + if (parent_tree == tree && parent_node == node) + { + ensure_unprelighted (tree_view); + break; + } + parent_node = parent_tree->parent_node; + parent_tree = parent_tree->parent_tree; + } + } + + TREE_VIEW_INTERNAL_ASSERT (gtk_tree_model_iter_children (priv->model, &children, &iter), FALSE); + + for (list = priv->columns; list; list = list->next) + { + GtkTreeViewColumn *column = list->data; + + if (gtk_tree_view_column_get_visible (column) == FALSE) + continue; + if (gtk_tree_view_column_get_sizing (column) == GTK_TREE_VIEW_COLUMN_AUTOSIZE) + _gtk_tree_view_column_cell_set_dirty (column, TRUE); + } + + if (priv->cursor_node) + { + cursor_changed = (node->children == priv->cursor_tree) + || gtk_tree_rbtree_contains (node->children, priv->cursor_tree); + } + else + cursor_changed = FALSE; + + if (gtk_tree_row_reference_valid (priv->anchor)) + { + GtkTreePath *anchor_path = gtk_tree_row_reference_get_path (priv->anchor); + if (gtk_tree_path_is_ancestor (path, anchor_path)) + { + gtk_tree_row_reference_free (priv->anchor); + priv->anchor = NULL; + } + gtk_tree_path_free (anchor_path); + } + + selection_changed = gtk_tree_view_unref_and_check_selection_tree (tree_view, node->children); + + /* Stop a pending double click */ + gtk_event_controller_reset (GTK_EVENT_CONTROLLER (priv->click_gesture)); + + gtk_tree_rbtree_remove (node->children); + + if (cursor_changed) + gtk_tree_view_real_set_cursor (tree_view, path, CLEAR_AND_SELECT | CURSOR_INVALID); + if (selection_changed) + g_signal_emit_by_name (priv->selection, "changed"); + + if (gtk_widget_get_mapped (GTK_WIDGET (tree_view))) + { + gtk_widget_queue_resize (GTK_WIDGET (tree_view)); + } + + g_signal_emit (tree_view, tree_view_signals[ROW_COLLAPSED], 0, &iter, path); + + if (gtk_widget_get_mapped (GTK_WIDGET (tree_view))) + update_prelight (tree_view, + priv->event_last_x, + priv->event_last_y); + + return TRUE; +} + +/** + * gtk_tree_view_collapse_row: + * @tree_view: a `GtkTreeView` + * @path: path to a row in the @tree_view + * + * Collapses a row (hides its child rows, if they exist). + * + * Returns: %TRUE if the row was collapsed. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +gboolean +gtk_tree_view_collapse_row (GtkTreeView *tree_view, + GtkTreePath *path) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreeRBTree *tree; + GtkTreeRBNode *node; + + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); + g_return_val_if_fail (priv->tree != NULL, FALSE); + g_return_val_if_fail (path != NULL, FALSE); + + if (_gtk_tree_view_find_node (tree_view, + path, + &tree, + &node)) + return FALSE; + + if (tree == NULL || node->children == NULL) + return FALSE; + + return gtk_tree_view_real_collapse_row (tree_view, path, tree, node); +} + +static void +gtk_tree_view_map_expanded_rows_helper (GtkTreeView *tree_view, + GtkTreeRBTree *tree, + GtkTreePath *path, + GtkTreeViewMappingFunc func, + gpointer user_data) +{ + GtkTreeRBNode *node; + + if (tree == NULL || tree->root == NULL) + return; + + node = gtk_tree_rbtree_first (tree); + + while (node) + { + if (node->children) + { + (* func) (tree_view, path, user_data); + gtk_tree_path_down (path); + gtk_tree_view_map_expanded_rows_helper (tree_view, node->children, path, func, user_data); + gtk_tree_path_up (path); + } + gtk_tree_path_next (path); + node = gtk_tree_rbtree_next (tree, node); + } +} + +/** + * gtk_tree_view_map_expanded_rows: + * @tree_view: A `GtkTreeView` + * @func: (scope call): A function to be called + * @data: User data to be passed to the function. + * + * Calls @func on all expanded rows. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +void +gtk_tree_view_map_expanded_rows (GtkTreeView *tree_view, + GtkTreeViewMappingFunc func, + gpointer user_data) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreePath *path; + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + g_return_if_fail (func != NULL); + + path = gtk_tree_path_new_first (); + + gtk_tree_view_map_expanded_rows_helper (tree_view, + priv->tree, + path, func, user_data); + + gtk_tree_path_free (path); +} + +/** + * gtk_tree_view_row_expanded: + * @tree_view: A `GtkTreeView`. + * @path: A `GtkTreePath` to test expansion state. + * + * Returns %TRUE if the node pointed to by @path is expanded in @tree_view. + * + * Returns: %TRUE if #path is expanded. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +gboolean +gtk_tree_view_row_expanded (GtkTreeView *tree_view, + GtkTreePath *path) +{ + GtkTreeRBTree *tree; + GtkTreeRBNode *node; + + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); + g_return_val_if_fail (path != NULL, FALSE); + + _gtk_tree_view_find_node (tree_view, path, &tree, &node); + + if (node == NULL) + return FALSE; + + return (node->children != NULL); +} + +/** + * gtk_tree_view_get_reorderable: + * @tree_view: a `GtkTreeView` + * + * Retrieves whether the user can reorder the tree via drag-and-drop. See + * gtk_tree_view_set_reorderable(). + * + * Returns: %TRUE if the tree can be reordered. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +gboolean +gtk_tree_view_get_reorderable (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); + + return priv->reorderable; +} + +/** + * gtk_tree_view_set_reorderable: + * @tree_view: A `GtkTreeView`. + * @reorderable: %TRUE, if the tree can be reordered. + * + * This function is a convenience function to allow you to reorder + * models that support the `GtkTreeDragSourceIface` and the + * `GtkTreeDragDestIface`. Both `GtkTreeStore` and `GtkListStore` support + * these. If @reorderable is %TRUE, then the user can reorder the + * model by dragging and dropping rows. The developer can listen to + * these changes by connecting to the model’s `GtkTreeModel::row-inserted` + * and `GtkTreeModel::row-deleted` signals. The reordering is implemented + * by setting up the tree view as a drag source and destination. + * Therefore, drag and drop can not be used in a reorderable view for any + * other purpose. + * + * This function does not give you any degree of control over the order -- any + * reordering is allowed. If more control is needed, you should probably + * handle drag and drop manually. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +void +gtk_tree_view_set_reorderable (GtkTreeView *tree_view, + gboolean reorderable) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + reorderable = reorderable != FALSE; + + if (priv->reorderable == reorderable) + return; + + if (reorderable) + { + GdkContentFormats *formats; + + formats = gdk_content_formats_new_for_gtype (GTK_TYPE_TREE_ROW_DATA); + + gtk_tree_view_enable_model_drag_source (tree_view, + GDK_BUTTON1_MASK, + formats, + GDK_ACTION_MOVE); + gtk_tree_view_enable_model_drag_dest (tree_view, + formats, + GDK_ACTION_MOVE); + gdk_content_formats_unref (formats); + } + else + { + gtk_tree_view_unset_rows_drag_source (tree_view); + gtk_tree_view_unset_rows_drag_dest (tree_view); + } + + priv->reorderable = reorderable; + + g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_REORDERABLE]); +} + +static void +gtk_tree_view_real_set_cursor (GtkTreeView *tree_view, + GtkTreePath *path, + SetCursorFlags flags) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + if (!(flags & CURSOR_INVALID) && priv->cursor_node) + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + + /* One cannot set the cursor on a separator. Also, if + * _gtk_tree_view_find_node returns TRUE, it ran out of tree + * before finding the tree and node belonging to path. The + * path maps to a non-existing path and we will silently bail out. + * We unset tree and node to avoid further processing. + */ + if (path == NULL || + row_is_separator (tree_view, NULL, path) + || _gtk_tree_view_find_node (tree_view, + path, + &priv->cursor_tree, + &priv->cursor_node)) + { + priv->cursor_tree = NULL; + priv->cursor_node = NULL; + } + + if (priv->cursor_node != NULL) + { + GtkTreeRBTree *new_tree = NULL; + GtkTreeRBNode *new_node = NULL; + + if ((flags & CLEAR_AND_SELECT) && !priv->modify_selection_pressed) + { + GtkTreeSelectMode mode = 0; + + if (priv->extend_selection_pressed) + mode |= GTK_TREE_SELECT_MODE_EXTEND; + + _gtk_tree_selection_internal_select_node (priv->selection, + priv->cursor_node, + priv->cursor_tree, + path, + mode, + FALSE); + } + + /* We have to re-find tree and node here again, somebody might have + * cleared the node or the whole tree in the GtkTreeSelection::changed + * callback. If the nodes differ we bail out here. + */ + _gtk_tree_view_find_node (tree_view, path, &new_tree, &new_node); + + if (priv->cursor_node == NULL || + priv->cursor_node != new_node) + return; + + if (flags & CLAMP_NODE) + { + gtk_tree_view_clamp_node_visible (tree_view, + priv->cursor_tree, + priv->cursor_node); + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + } + } + + if (!gtk_widget_in_destruction (GTK_WIDGET (tree_view))) + g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0); +} + +/** + * gtk_tree_view_get_cursor: + * @tree_view: A `GtkTreeView` + * @path: (out) (transfer full) (optional) (nullable): A pointer to be + * filled with the current cursor path + * @focus_column: (out) (transfer none) (optional) (nullable): A + * pointer to be filled with the current focus column + * + * Fills in @path and @focus_column with the current path and focus column. If + * the cursor isn’t currently set, then *@path will be %NULL. If no column + * currently has focus, then *@focus_column will be %NULL. + * + * The returned `GtkTreePath` must be freed with gtk_tree_path_free() when + * you are done with it. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +void +gtk_tree_view_get_cursor (GtkTreeView *tree_view, + GtkTreePath **path, + GtkTreeViewColumn **focus_column) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + if (path) + { + if (priv->cursor_node) + *path = _gtk_tree_path_new_from_rbtree (priv->cursor_tree, + priv->cursor_node); + else + *path = NULL; + } + + if (focus_column) + { + *focus_column = priv->focus_column; + } +} + +/** + * gtk_tree_view_set_cursor: + * @tree_view: A `GtkTreeView` + * @path: A `GtkTreePath` + * @focus_column: (nullable): A `GtkTreeViewColumn` + * @start_editing: %TRUE if the specified cell should start being edited. + * + * Sets the current keyboard focus to be at @path, and selects it. This is + * useful when you want to focus the user’s attention on a particular row. If + * @focus_column is not %NULL, then focus is given to the column specified by + * it. Additionally, if @focus_column is specified, and @start_editing is + * %TRUE, then editing should be started in the specified cell. + * This function is often followed by @gtk_widget_grab_focus (@tree_view) + * in order to give keyboard focus to the widget. Please note that editing + * can only happen when the widget is realized. + * + * If @path is invalid for @model, the current cursor (if any) will be unset + * and the function will return without failing. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +void +gtk_tree_view_set_cursor (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *focus_column, + gboolean start_editing) +{ + gtk_tree_view_set_cursor_on_cell (tree_view, path, focus_column, + NULL, start_editing); +} + +/** + * gtk_tree_view_set_cursor_on_cell: + * @tree_view: A `GtkTreeView` + * @path: A `GtkTreePath` + * @focus_column: (nullable): A `GtkTreeViewColumn` + * @focus_cell: (nullable): A `GtkCellRenderer` + * @start_editing: %TRUE if the specified cell should start being edited. + * + * Sets the current keyboard focus to be at @path, and selects it. This is + * useful when you want to focus the user’s attention on a particular row. If + * @focus_column is not %NULL, then focus is given to the column specified by + * it. If @focus_column and @focus_cell are not %NULL, and @focus_column + * contains 2 or more editable or activatable cells, then focus is given to + * the cell specified by @focus_cell. Additionally, if @focus_column is + * specified, and @start_editing is %TRUE, then editing should be started in + * the specified cell. This function is often followed by + * @gtk_widget_grab_focus (@tree_view) in order to give keyboard focus to the + * widget. Please note that editing can only happen when the widget is + * realized. + * + * If @path is invalid for @model, the current cursor (if any) will be unset + * and the function will return without failing. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +void +gtk_tree_view_set_cursor_on_cell (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *focus_column, + GtkCellRenderer *focus_cell, + gboolean start_editing) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + g_return_if_fail (path != NULL); + g_return_if_fail (focus_column == NULL || GTK_IS_TREE_VIEW_COLUMN (focus_column)); + + if (!priv->model) + return; + + if (focus_cell) + { + g_return_if_fail (focus_column); + g_return_if_fail (GTK_IS_CELL_RENDERER (focus_cell)); + } + + /* cancel the current editing, if it exists */ + if (priv->edited_column && + gtk_cell_area_get_edit_widget + (gtk_cell_layout_get_area (GTK_CELL_LAYOUT (priv->edited_column)))) + gtk_tree_view_stop_editing (tree_view, TRUE); + + gtk_tree_view_real_set_cursor (tree_view, path, CLEAR_AND_SELECT | CLAMP_NODE); + + if (focus_column && + gtk_tree_view_column_get_visible (focus_column)) + { +#ifndef G_DISABLE_CHECKS + GList *list; + gboolean column_in_tree = FALSE; + + for (list = priv->columns; list; list = list->next) + if (list->data == focus_column) + { + column_in_tree = TRUE; + break; + } + g_return_if_fail (column_in_tree); +#endif + _gtk_tree_view_set_focus_column (tree_view, focus_column); + if (focus_cell) + gtk_tree_view_column_focus_cell (focus_column, focus_cell); + if (start_editing) + gtk_tree_view_start_editing (tree_view, path, TRUE); + } +} + +/** + * gtk_tree_view_get_path_at_pos: + * @tree_view: A `GtkTreeView`. + * @x: The x position to be identified (relative to bin_window). + * @y: The y position to be identified (relative to bin_window). + * @path: (out) (optional) (nullable): A pointer to a `GtkTreePath` + * pointer to be filled in + * @column: (out) (transfer none) (optional) (nullable): A pointer to + * a `GtkTreeViewColumn` pointer to be filled in + * @cell_x: (out) (optional): A pointer where the X coordinate + * relative to the cell can be placed + * @cell_y: (out) (optional): A pointer where the Y coordinate + * relative to the cell can be placed + * + * Finds the path at the point (@x, @y), relative to bin_window coordinates. + * That is, @x and @y are relative to an events coordinates. Widget-relative + * coordinates must be converted using + * gtk_tree_view_convert_widget_to_bin_window_coords(). It is primarily for + * things like popup menus. If @path is non-%NULL, then it will be filled + * with the `GtkTreePath` at that point. This path should be freed with + * gtk_tree_path_free(). If @column is non-%NULL, then it will be filled + * with the column at that point. @cell_x and @cell_y return the coordinates + * relative to the cell background (i.e. the @background_area passed to + * gtk_cell_renderer_render()). This function is only meaningful if + * @tree_view is realized. Therefore this function will always return %FALSE + * if @tree_view is not realized or does not have a model. + * + * For converting widget coordinates (eg. the ones you get from + * GtkWidget::query-tooltip), please see + * gtk_tree_view_convert_widget_to_bin_window_coords(). + * + * Returns: %TRUE if a row exists at that coordinate. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +gboolean +gtk_tree_view_get_path_at_pos (GtkTreeView *tree_view, + int x, + int y, + GtkTreePath **path, + GtkTreeViewColumn **column, + int *cell_x, + int *cell_y) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreeRBTree *tree; + GtkTreeRBNode *node; + int y_offset; + + g_return_val_if_fail (tree_view != NULL, FALSE); + + if (path) + *path = NULL; + if (column) + *column = NULL; + + if (priv->tree == NULL) + return FALSE; + + if (x > gtk_adjustment_get_upper (priv->hadjustment)) + return FALSE; + + if (x < 0 || y < 0) + return FALSE; + + if (column || cell_x) + { + GtkTreeViewColumn *tmp_column; + GtkTreeViewColumn *last_column = NULL; + GList *list; + int remaining_x = x; + gboolean found = FALSE; + gboolean rtl; + int width; + + rtl = (_gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); + for (list = (rtl ? g_list_last (priv->columns) : g_list_first (priv->columns)); + list; + list = (rtl ? list->prev : list->next)) + { + tmp_column = list->data; + + if (gtk_tree_view_column_get_visible (tmp_column) == FALSE) + continue; + + last_column = tmp_column; + width = gtk_tree_view_column_get_width (tmp_column); + if (remaining_x < width) + { + found = TRUE; + + if (column) + *column = tmp_column; + + if (cell_x) + *cell_x = remaining_x; + + break; + } + remaining_x -= width; + } + + /* If found is FALSE and there is a last_column, then it the remainder + * space is in that area + */ + if (!found) + { + if (last_column) + { + if (column) + *column = last_column; + + if (cell_x) + *cell_x = gtk_tree_view_column_get_width (last_column) + remaining_x; + } + else + { + return FALSE; + } + } + } + + y_offset = gtk_tree_rbtree_find_offset (priv->tree, + TREE_WINDOW_Y_TO_RBTREE_Y (priv, y), + &tree, &node); + + if (tree == NULL) + return FALSE; + + if (cell_y) + *cell_y = y_offset; + + if (path) + *path = _gtk_tree_path_new_from_rbtree (tree, node); + + return TRUE; +} + + +static inline int +gtk_tree_view_get_cell_area_height (GtkTreeView *tree_view, + GtkTreeRBNode *node) +{ + int expander_size = gtk_tree_view_get_expander_size (tree_view); + int height; + + /* The "cell" areas are the cell_area passed in to gtk_cell_renderer_render(), + * i.e. just the cells, no spacing. + * + * The cell area height is at least expander_size - vertical_separator. + * For regular nodes, the height is then at least expander_size. We should + * be able to enforce the expander_size minimum here, because this + * function will not be called for irregular (e.g. separator) rows. + */ + height = gtk_tree_view_get_row_height (tree_view, node); + if (height < expander_size) + height = expander_size; + + return height; +} + +static inline int +gtk_tree_view_get_cell_area_y_offset (GtkTreeView *tree_view, + GtkTreeRBTree *tree, + GtkTreeRBNode *node) +{ + int offset; + + offset = gtk_tree_view_get_row_y_offset (tree_view, tree, node); + + return offset; +} + +/** + * gtk_tree_view_get_cell_area: + * @tree_view: a `GtkTreeView` + * @path: (nullable): a `GtkTreePath` for the row, or %NULL to get only horizontal coordinates + * @column: (nullable): a `GtkTreeViewColumn` for the column, or %NULL to get only vertical coordinates + * @rect: (out): rectangle to fill with cell rect + * + * Fills the bounding rectangle in bin_window coordinates for the cell at the + * row specified by @path and the column specified by @column. If @path is + * %NULL, or points to a path not currently displayed, the @y and @height fields + * of the rectangle will be filled with 0. If @column is %NULL, the @x and @width + * fields will be filled with 0. The sum of all cell rects does not cover the + * entire tree; there are extra pixels in between rows, for example. The + * returned rectangle is equivalent to the @cell_area passed to + * gtk_cell_renderer_render(). This function is only valid if @tree_view is + * realized. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +void +gtk_tree_view_get_cell_area (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + GdkRectangle *rect) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreeRBTree *tree = NULL; + GtkTreeRBNode *node = NULL; + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + g_return_if_fail (column == NULL || GTK_IS_TREE_VIEW_COLUMN (column)); + g_return_if_fail (rect != NULL); + g_return_if_fail (!column || gtk_tree_view_column_get_tree_view (column) == (GtkWidget *) tree_view); + g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view))); + + rect->x = 0; + rect->y = 0; + rect->width = 0; + rect->height = 0; + + if (column) + { + rect->x = gtk_tree_view_column_get_x_offset (column) + _TREE_VIEW_HORIZONTAL_SEPARATOR / 2; + rect->width = gtk_tree_view_column_get_width (column) - _TREE_VIEW_HORIZONTAL_SEPARATOR; + } + + if (path) + { + gboolean ret = _gtk_tree_view_find_node (tree_view, path, &tree, &node); + + /* Get vertical coords */ + if ((!ret && tree == NULL) || ret) + return; + + if (row_is_separator (tree_view, NULL, path)) + { + /* There isn't really a "cell area" for separator, so we + * return the y, height values for background area instead. + */ + rect->y = gtk_tree_view_get_row_y_offset (tree_view, tree, node); + rect->height = gtk_tree_view_get_row_height (tree_view, node); + } + else + { + rect->y = gtk_tree_view_get_cell_area_y_offset (tree_view, tree, node); + rect->height = gtk_tree_view_get_cell_area_height (tree_view, node); + } + + if (column && + gtk_tree_view_is_expander_column (tree_view, column)) + { + int depth = gtk_tree_path_get_depth (path); + gboolean rtl; + + rtl = _gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL; + + if (!rtl) + rect->x += (depth - 1) * priv->level_indentation; + rect->width -= (depth - 1) * priv->level_indentation; + + if (gtk_tree_view_draw_expanders (tree_view)) + { + int expander_size = gtk_tree_view_get_expander_size (tree_view); + if (!rtl) + rect->x += depth * expander_size; + rect->width -= depth * expander_size; + } + + rect->width = MAX (rect->width, 0); + } + } +} + +static inline int +gtk_tree_view_get_row_height (GtkTreeView *tree_view, + GtkTreeRBNode *node) +{ + int expander_size = gtk_tree_view_get_expander_size (tree_view); + int height; + + /* The "background" areas of all rows/cells add up to cover the entire tree. + * The background includes all inter-row and inter-cell spacing. + * + * If the row pointed at by node does not have a height set, we default + * to expander_size, which is the minimum height for regular nodes. + * Non-regular nodes (e.g. separators) can have a height set smaller + * than expander_size and should not be overruled here. + */ + height = GTK_TREE_RBNODE_GET_HEIGHT (node); + if (height <= 0) + height = expander_size; + + return height; +} + +static inline int +gtk_tree_view_get_row_y_offset (GtkTreeView *tree_view, + GtkTreeRBTree *tree, + GtkTreeRBNode *node) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + int offset; + + offset = gtk_tree_rbtree_node_find_offset (tree, node); + + return RBTREE_Y_TO_TREE_WINDOW_Y (priv, offset); +} + +/** + * gtk_tree_view_get_background_area: + * @tree_view: a `GtkTreeView` + * @path: (nullable): a `GtkTreePath` for the row, or %NULL to get only horizontal coordinates + * @column: (nullable): a `GtkTreeViewColumn` for the column, or %NULL to get only vertical coordinates + * @rect: (out): rectangle to fill with cell background rect + * + * Fills the bounding rectangle in bin_window coordinates for the cell at the + * row specified by @path and the column specified by @column. If @path is + * %NULL, or points to a node not found in the tree, the @y and @height fields of + * the rectangle will be filled with 0. If @column is %NULL, the @x and @width + * fields will be filled with 0. The returned rectangle is equivalent to the + * @background_area passed to gtk_cell_renderer_render(). These background + * areas tile to cover the entire bin window. Contrast with the @cell_area, + * returned by gtk_tree_view_get_cell_area(), which returns only the cell + * itself, excluding surrounding borders and the tree expander area. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +void +gtk_tree_view_get_background_area (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + GdkRectangle *rect) +{ + GtkTreeRBTree *tree = NULL; + GtkTreeRBNode *node = NULL; + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + g_return_if_fail (column == NULL || GTK_IS_TREE_VIEW_COLUMN (column)); + g_return_if_fail (rect != NULL); + + rect->x = 0; + rect->y = 0; + rect->width = 0; + rect->height = 0; + + if (path) + { + /* Get vertical coords */ + + if (!_gtk_tree_view_find_node (tree_view, path, &tree, &node) && + tree == NULL) + return; + + rect->y = gtk_tree_view_get_row_y_offset (tree_view, tree, node); + rect->height = gtk_tree_view_get_row_height (tree_view, node); + } + + if (column) + { + int x2 = 0; + + gtk_tree_view_get_background_xrange (tree_view, tree, column, &rect->x, &x2); + rect->width = x2 - rect->x; + } +} + +/** + * gtk_tree_view_get_visible_rect: + * @tree_view: a `GtkTreeView` + * @visible_rect: (out): rectangle to fill + * + * Fills @visible_rect with the currently-visible region of the + * buffer, in tree coordinates. Convert to bin_window coordinates with + * gtk_tree_view_convert_tree_to_bin_window_coords(). + * Tree coordinates start at 0,0 for row 0 of the tree, and cover the entire + * scrollable area of the tree. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +void +gtk_tree_view_get_visible_rect (GtkTreeView *tree_view, + GdkRectangle *visible_rect) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkAllocation allocation; + GtkWidget *widget; + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + widget = GTK_WIDGET (tree_view); + + if (visible_rect) + { + gtk_widget_get_allocation (widget, &allocation); + visible_rect->x = gtk_adjustment_get_value (priv->hadjustment); + visible_rect->y = gtk_adjustment_get_value (priv->vadjustment); + visible_rect->width = allocation.width; + visible_rect->height = allocation.height - gtk_tree_view_get_effective_header_height (tree_view); + } +} + +/** + * gtk_tree_view_convert_widget_to_tree_coords: + * @tree_view: a `GtkTreeView` + * @wx: X coordinate relative to the widget + * @wy: Y coordinate relative to the widget + * @tx: (out): return location for tree X coordinate + * @ty: (out): return location for tree Y coordinate + * + * Converts widget coordinates to coordinates for the + * tree (the full scrollable area of the tree). + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +void +gtk_tree_view_convert_widget_to_tree_coords (GtkTreeView *tree_view, + int wx, + int wy, + int *tx, + int *ty) +{ + int x, y; + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, + wx, wy, + &x, &y); + gtk_tree_view_convert_bin_window_to_tree_coords (tree_view, + x, y, + tx, ty); +} + +/** + * gtk_tree_view_convert_tree_to_widget_coords: + * @tree_view: a `GtkTreeView` + * @tx: X coordinate relative to the tree + * @ty: Y coordinate relative to the tree + * @wx: (out): return location for widget X coordinate + * @wy: (out): return location for widget Y coordinate + * + * Converts tree coordinates (coordinates in full scrollable area of the tree) + * to widget coordinates. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +void +gtk_tree_view_convert_tree_to_widget_coords (GtkTreeView *tree_view, + int tx, + int ty, + int *wx, + int *wy) +{ + int x, y; + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + gtk_tree_view_convert_tree_to_bin_window_coords (tree_view, + tx, ty, + &x, &y); + gtk_tree_view_convert_bin_window_to_widget_coords (tree_view, + x, y, + wx, wy); +} + +/** + * gtk_tree_view_convert_widget_to_bin_window_coords: + * @tree_view: a `GtkTreeView` + * @wx: X coordinate relative to the widget + * @wy: Y coordinate relative to the widget + * @bx: (out): return location for bin_window X coordinate + * @by: (out): return location for bin_window Y coordinate + * + * Converts widget coordinates to coordinates for the bin_window. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +void +gtk_tree_view_convert_widget_to_bin_window_coords (GtkTreeView *tree_view, + int wx, + int wy, + int *bx, + int *by) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + if (bx) + *bx = wx + gtk_adjustment_get_value (priv->hadjustment); + if (by) + *by = wy - gtk_tree_view_get_effective_header_height (tree_view); +} + +/** + * gtk_tree_view_convert_bin_window_to_widget_coords: + * @tree_view: a `GtkTreeView` + * @bx: bin_window X coordinate + * @by: bin_window Y coordinate + * @wx: (out): return location for widget X coordinate + * @wy: (out): return location for widget Y coordinate + * + * Converts bin_window coordinates to widget relative coordinates. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +void +gtk_tree_view_convert_bin_window_to_widget_coords (GtkTreeView *tree_view, + int bx, + int by, + int *wx, + int *wy) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + if (wx) + *wx = bx - gtk_adjustment_get_value (priv->hadjustment); + if (wy) + *wy = by + gtk_tree_view_get_effective_header_height (tree_view); +} + +/** + * gtk_tree_view_convert_tree_to_bin_window_coords: + * @tree_view: a `GtkTreeView` + * @tx: tree X coordinate + * @ty: tree Y coordinate + * @bx: (out): return location for X coordinate relative to bin_window + * @by: (out): return location for Y coordinate relative to bin_window + * + * Converts tree coordinates (coordinates in full scrollable area of the tree) + * to bin_window coordinates. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +void +gtk_tree_view_convert_tree_to_bin_window_coords (GtkTreeView *tree_view, + int tx, + int ty, + int *bx, + int *by) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + if (bx) + *bx = tx; + if (by) + *by = ty - priv->dy; +} + +/** + * gtk_tree_view_convert_bin_window_to_tree_coords: + * @tree_view: a `GtkTreeView` + * @bx: X coordinate relative to bin_window + * @by: Y coordinate relative to bin_window + * @tx: (out): return location for tree X coordinate + * @ty: (out): return location for tree Y coordinate + * + * Converts bin_window coordinates to coordinates for the + * tree (the full scrollable area of the tree). + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +void +gtk_tree_view_convert_bin_window_to_tree_coords (GtkTreeView *tree_view, + int bx, + int by, + int *tx, + int *ty) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + if (tx) + *tx = bx; + if (ty) + *ty = by + priv->dy; +} + + + +/** + * gtk_tree_view_get_visible_range: + * @tree_view: A `GtkTreeView` + * @start_path: (out) (optional): Return location for start of region + * @end_path: (out) (optional): Return location for end of region + * + * Sets @start_path and @end_path to be the first and last visible path. + * Note that there may be invisible paths in between. + * + * The paths should be freed with gtk_tree_path_free() after use. + * + * Returns: %TRUE, if valid paths were placed in @start_path and @end_path. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + */ +gboolean +gtk_tree_view_get_visible_range (GtkTreeView *tree_view, + GtkTreePath **start_path, + GtkTreePath **end_path) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreeRBTree *tree; + GtkTreeRBNode *node; + gboolean retval; + + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); + + if (!priv->tree) + return FALSE; + + retval = TRUE; + + if (start_path) + { + gtk_tree_rbtree_find_offset (priv->tree, + TREE_WINDOW_Y_TO_RBTREE_Y (priv, 0), + &tree, &node); + if (node) + *start_path = _gtk_tree_path_new_from_rbtree (tree, node); + else + retval = FALSE; + } + + if (end_path) + { + int y; + + if (gtk_tree_view_get_height (tree_view) < gtk_adjustment_get_page_size (priv->vadjustment)) + y = gtk_tree_view_get_height (tree_view) - 1; + else + y = TREE_WINDOW_Y_TO_RBTREE_Y (priv, gtk_adjustment_get_page_size (priv->vadjustment)) - 1; + + gtk_tree_rbtree_find_offset (priv->tree, y, &tree, &node); + if (node) + *end_path = _gtk_tree_path_new_from_rbtree (tree, node); + else + retval = FALSE; + } + + return retval; +} + +/** + * gtk_tree_view_is_blank_at_pos: + * @tree_view: A `GtkTreeView` + * @x: The x position to be identified (relative to bin_window) + * @y: The y position to be identified (relative to bin_window) + * @path: (out) (optional) (nullable): A pointer to a `GtkTreePath` pointer to + * be filled in + * @column: (out) (transfer none) (optional) (nullable): A pointer to a + * `GtkTreeViewColumn` pointer to be filled in + * @cell_x: (out) (optional): A pointer where the X coordinate relative to the + * cell can be placed + * @cell_y: (out) (optional): A pointer where the Y coordinate relative to the + * cell can be placed + * + * Determine whether the point (@x, @y) in @tree_view is blank, that is no + * cell content nor an expander arrow is drawn at the location. If so, the + * location can be considered as the background. You might wish to take + * special action on clicks on the background, such as clearing a current + * selection, having a custom context menu or starting rubber banding. + * + * The @x and @y coordinate that are provided must be relative to bin_window + * coordinates. Widget-relative coordinates must be converted using + * gtk_tree_view_convert_widget_to_bin_window_coords(). + * + * For converting widget coordinates (eg. the ones you get from + * GtkWidget::query-tooltip), please see + * gtk_tree_view_convert_widget_to_bin_window_coords(). + * + * The @path, @column, @cell_x and @cell_y arguments will be filled in + * likewise as for gtk_tree_view_get_path_at_pos(). Please see + * gtk_tree_view_get_path_at_pos() for more information. + * + * Returns: %TRUE if the area at the given coordinates is blank, + * %FALSE otherwise. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + */ +gboolean +gtk_tree_view_is_blank_at_pos (GtkTreeView *tree_view, + int x, + int y, + GtkTreePath **path, + GtkTreeViewColumn **column, + int *cell_x, + int *cell_y) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreeRBTree *tree; + GtkTreeRBNode *node; + GtkTreeIter iter; + GtkTreePath *real_path; + GtkTreeViewColumn *real_column; + GdkRectangle cell_area, background_area; + + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); + + if (!gtk_tree_view_get_path_at_pos (tree_view, x, y, + &real_path, &real_column, + cell_x, cell_y)) + /* If there's no path here, it is blank */ + return TRUE; + + if (path) + *path = real_path; + + if (column) + *column = real_column; + + gtk_tree_model_get_iter (priv->model, &iter, real_path); + _gtk_tree_view_find_node (tree_view, real_path, &tree, &node); + + /* Check if there's an expander arrow at (x, y) */ + if (real_column == priv->expander_column + && gtk_tree_view_draw_expanders (tree_view)) + { + gboolean over_arrow; + + over_arrow = coords_are_over_arrow (tree_view, tree, node, x, y); + + if (over_arrow) + { + if (!path) + gtk_tree_path_free (real_path); + return FALSE; + } + } + + /* Otherwise, have the column see if there's a cell at (x, y) */ + gtk_tree_view_column_cell_set_cell_data (real_column, + priv->model, + &iter, + GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_PARENT), + node->children ? TRUE : FALSE); + + gtk_tree_view_get_background_area (tree_view, real_path, real_column, + &background_area); + gtk_tree_view_get_cell_area (tree_view, real_path, real_column, + &cell_area); + + if (!path) + gtk_tree_path_free (real_path); + + return _gtk_tree_view_column_is_blank_at_pos (real_column, + &cell_area, + &background_area, + x, y); +} + +static void +unset_reorderable (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + if (priv->reorderable) + { + priv->reorderable = FALSE; + g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_REORDERABLE]); + } +} + +/** + * gtk_tree_view_enable_model_drag_source: + * @tree_view: a `GtkTreeView` + * @start_button_mask: Mask of allowed buttons to start drag + * @formats: the target formats that the drag will support + * @actions: the bitmask of possible actions for a drag from this + * widget + * + * Turns @tree_view into a drag source for automatic DND. Calling this + * method sets `GtkTreeView`:reorderable to %FALSE. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +void +gtk_tree_view_enable_model_drag_source (GtkTreeView *tree_view, + GdkModifierType start_button_mask, + GdkContentFormats *formats, + GdkDragAction actions) +{ + TreeViewDragInfo *di; + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + di = ensure_info (tree_view); + + di->source_formats = gdk_content_formats_ref (formats); + di->source_actions = actions; + di->drag = NULL; + + di->start_button_mask = start_button_mask; + di->source_set = TRUE; + + unset_reorderable (tree_view); +} + +/** + * gtk_tree_view_enable_model_drag_dest: + * @tree_view: a `GtkTreeView` + * @formats: the target formats that the drag will support + * @actions: the bitmask of possible actions for a drag from this + * widget + * + * Turns @tree_view into a drop destination for automatic DND. Calling + * this method sets `GtkTreeView`:reorderable to %FALSE. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +void +gtk_tree_view_enable_model_drag_dest (GtkTreeView *tree_view, + GdkContentFormats *formats, + GdkDragAction actions) +{ + TreeViewDragInfo *di; + GtkCssNode *widget_node; + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + di = ensure_info (tree_view); + di->dest_set = TRUE; + + di->dest = gtk_drop_target_async_new (gdk_content_formats_ref (formats), actions); + g_signal_connect (di->dest, "drag-leave", G_CALLBACK (gtk_tree_view_drag_leave), tree_view); + g_signal_connect (di->dest, "drag-enter", G_CALLBACK (gtk_tree_view_drag_motion), tree_view); + g_signal_connect (di->dest, "drag-motion", G_CALLBACK (gtk_tree_view_drag_motion), tree_view); + g_signal_connect (di->dest, "drop", G_CALLBACK (gtk_tree_view_drag_drop), tree_view); + gtk_widget_add_controller (GTK_WIDGET (tree_view), GTK_EVENT_CONTROLLER (di->dest)); + g_object_ref (di->dest); + + widget_node = gtk_widget_get_css_node (GTK_WIDGET (tree_view)); + di->cssnode = gtk_css_node_new (); + gtk_css_node_set_name (di->cssnode, g_quark_from_static_string ("dndtarget")); + gtk_css_node_set_parent (di->cssnode, widget_node); + gtk_css_node_set_state (di->cssnode, gtk_css_node_get_state (widget_node)); + g_object_unref (di->cssnode); + + unset_reorderable (tree_view); +} + +/** + * gtk_tree_view_unset_rows_drag_source: + * @tree_view: a `GtkTreeView` + * + * Undoes the effect of + * gtk_tree_view_enable_model_drag_source(). Calling this method sets + * `GtkTreeView`:reorderable to %FALSE. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +void +gtk_tree_view_unset_rows_drag_source (GtkTreeView *tree_view) +{ + TreeViewDragInfo *di; + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + di = get_info (tree_view); + + if (di) + { + if (di->source_set) + { + g_clear_pointer (&di->source_formats, gdk_content_formats_unref); + di->source_set = FALSE; + } + + if (!di->dest_set && !di->source_set) + remove_info (tree_view); + } + + unset_reorderable (tree_view); +} + +/** + * gtk_tree_view_unset_rows_drag_dest: + * @tree_view: a `GtkTreeView` + * + * Undoes the effect of + * gtk_tree_view_enable_model_drag_dest(). Calling this method sets + * `GtkTreeView`:reorderable to %FALSE. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +void +gtk_tree_view_unset_rows_drag_dest (GtkTreeView *tree_view) +{ + TreeViewDragInfo *di; + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + di = get_info (tree_view); + + if (di) + { + if (di->dest_set) + { + gtk_widget_remove_controller (GTK_WIDGET (tree_view), GTK_EVENT_CONTROLLER (di->dest)); + di->dest = NULL; + di->dest_set = FALSE; + + gtk_css_node_set_parent (di->cssnode, NULL); + di->cssnode = NULL; + } + + if (!di->dest_set && !di->source_set) + remove_info (tree_view); + } + + unset_reorderable (tree_view); +} + +/** + * gtk_tree_view_set_drag_dest_row: + * @tree_view: a `GtkTreeView` + * @path: (nullable): The path of the row to highlight + * @pos: Specifies whether to drop before, after or into the row + * + * Sets the row that is highlighted for feedback. + * If @path is %NULL, an existing highlight is removed. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + */ +void +gtk_tree_view_set_drag_dest_row (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewDropPosition pos) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreePath *current_dest; + + /* Note; this function is exported to allow a custom DND + * implementation, so it can't touch TreeViewDragInfo + */ + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + current_dest = NULL; + + if (priv->drag_dest_row) + { + current_dest = gtk_tree_row_reference_get_path (priv->drag_dest_row); + gtk_tree_row_reference_free (priv->drag_dest_row); + } + + /* special case a drop on an empty model */ + priv->empty_view_drop = 0; + + if (pos == GTK_TREE_VIEW_DROP_BEFORE && path + && gtk_tree_path_get_depth (path) == 1 + && gtk_tree_path_get_indices (path)[0] == 0) + { + int n_children; + + n_children = gtk_tree_model_iter_n_children (priv->model, + NULL); + + if (!n_children) + priv->empty_view_drop = 1; + } + + priv->drag_dest_pos = pos; + + if (path) + { + priv->drag_dest_row = + gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), priv->model, path); + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + } + else + priv->drag_dest_row = NULL; + + if (current_dest) + { + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + + gtk_tree_path_free (current_dest); + } +} + +/** + * gtk_tree_view_get_drag_dest_row: + * @tree_view: a `GtkTreeView` + * @path: (out) (optional) (nullable): Return location for the path of the highlighted row + * @pos: (out) (optional): Return location for the drop position + * + * Gets information about the row that is highlighted for feedback. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +void +gtk_tree_view_get_drag_dest_row (GtkTreeView *tree_view, + GtkTreePath **path, + GtkTreeViewDropPosition *pos) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + if (path) + { + if (priv->drag_dest_row) + *path = gtk_tree_row_reference_get_path (priv->drag_dest_row); + else + { + if (priv->empty_view_drop) + *path = gtk_tree_path_new_from_indices (0, -1); + else + *path = NULL; + } + } + + if (pos) + *pos = priv->drag_dest_pos; +} + +/** + * gtk_tree_view_get_dest_row_at_pos: + * @tree_view: a `GtkTreeView` + * @drag_x: the position to determine the destination row for + * @drag_y: the position to determine the destination row for + * @path: (out) (optional) (nullable): Return location for the path of + * the highlighted row + * @pos: (out) (optional): Return location for the drop position, or + * %NULL + * + * Determines the destination row for a given position. @drag_x and + * @drag_y are expected to be in widget coordinates. This function is only + * meaningful if @tree_view is realized. Therefore this function will always + * return %FALSE if @tree_view is not realized or does not have a model. + * + * Returns: whether there is a row at the given position, %TRUE if this + * is indeed the case. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +gboolean +gtk_tree_view_get_dest_row_at_pos (GtkTreeView *tree_view, + int drag_x, + int drag_y, + GtkTreePath **path, + GtkTreeViewDropPosition *pos) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + int cell_y; + int bin_x, bin_y; + double offset_into_row; + double fourth; + GdkRectangle cell; + GtkTreeViewColumn *column = NULL; + GtkTreePath *tmp_path = NULL; + + /* Note; this function is exported to allow a custom DND + * implementation, so it can't touch TreeViewDragInfo + */ + + g_return_val_if_fail (tree_view != NULL, FALSE); + g_return_val_if_fail (drag_x >= 0, FALSE); + g_return_val_if_fail (drag_y >= 0, FALSE); + + if (path) + *path = NULL; + + if (priv->tree == NULL) + return FALSE; + + /* If in the top fourth of a row, we drop before that row; if + * in the bottom fourth, drop after that row; if in the middle, + * and the row has children, drop into the row. + */ + gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, drag_x, drag_y, + &bin_x, &bin_y); + + if (!gtk_tree_view_get_path_at_pos (tree_view, + bin_x, + bin_y, + &tmp_path, + &column, + NULL, + &cell_y)) + return FALSE; + + gtk_tree_view_get_background_area (tree_view, tmp_path, column, + &cell); + + offset_into_row = cell_y; + + if (path) + *path = tmp_path; + else + gtk_tree_path_free (tmp_path); + + tmp_path = NULL; + + fourth = cell.height / 4.0; + + if (pos) + { + if (offset_into_row < fourth) + { + *pos = GTK_TREE_VIEW_DROP_BEFORE; + } + else if (offset_into_row < (cell.height / 2.0)) + { + *pos = GTK_TREE_VIEW_DROP_INTO_OR_BEFORE; + } + else if (offset_into_row < cell.height - fourth) + { + *pos = GTK_TREE_VIEW_DROP_INTO_OR_AFTER; + } + else + { + *pos = GTK_TREE_VIEW_DROP_AFTER; + } + } + + return TRUE; +} + + +static void +gtk_treeview_snapshot_border (GtkSnapshot *snapshot, + const graphene_rect_t *rect) +{ + GskRoundedRect rounded; + + gsk_rounded_rect_init_from_rect (&rounded, rect, 0); + +#define BLACK { 0, 0, 0, 1 } + gtk_snapshot_append_border (snapshot, + &rounded, + (float[4]) { 1, 1, 1, 1 }, + (GdkRGBA[4]) { BLACK, BLACK, BLACK, BLACK }); +#undef BLACK +} + +/* KEEP IN SYNC WITH GTK_TREE_VIEW_BIN_EXPOSE */ +/** + * gtk_tree_view_create_row_drag_icon: + * @tree_view: a `GtkTreeView` + * @path: a `GtkTreePath` in @tree_view + * + * Creates a `cairo_surface_t` representation of the row at @path. + * This image is used for a drag icon. + * + * Returns: (transfer full) (nullable): a newly-allocated surface of the drag icon. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +GdkPaintable * +gtk_tree_view_create_row_drag_icon (GtkTreeView *tree_view, + GtkTreePath *path) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreeIter iter; + GtkTreeRBTree *tree; + GtkTreeRBNode *node; + GtkStyleContext *context; + int cell_offset; + GList *list; + GdkRectangle background_area; + GtkWidget *widget; + GtkSnapshot *snapshot; + GdkPaintable *paintable; + int depth; + /* start drawing inside the black outline */ + int x = 1, y = 1; + int bin_window_width; + gboolean is_separator = FALSE; + gboolean rtl; + + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL); + g_return_val_if_fail (path != NULL, NULL); + + widget = GTK_WIDGET (tree_view); + + if (!gtk_widget_get_realized (widget)) + return NULL; + + depth = gtk_tree_path_get_depth (path); + + _gtk_tree_view_find_node (tree_view, + path, + &tree, + &node); + + if (tree == NULL) + return NULL; + + if (!gtk_tree_model_get_iter (priv->model, + &iter, + path)) + return NULL; + + context = gtk_widget_get_style_context (widget); + + is_separator = row_is_separator (tree_view, &iter, NULL); + + cell_offset = x; + + background_area.y = y; + background_area.height = gtk_tree_view_get_row_height (tree_view, node); + + bin_window_width = gtk_widget_get_width (GTK_WIDGET (tree_view)); + + snapshot = gtk_snapshot_new (); + + gtk_snapshot_render_background (snapshot, context, + 0, 0, + bin_window_width + 2, + background_area.height + 2); + + rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL; + + for (list = (rtl ? g_list_last (priv->columns) : g_list_first (priv->columns)); + list; + list = (rtl ? list->prev : list->next)) + { + GtkTreeViewColumn *column = list->data; + GdkRectangle cell_area; + + if (!gtk_tree_view_column_get_visible (column)) + continue; + + gtk_tree_view_column_cell_set_cell_data (column, priv->model, &iter, + GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_PARENT), + node->children?TRUE:FALSE); + + background_area.x = cell_offset; + background_area.width = gtk_tree_view_column_get_width (column); + + cell_area = background_area; + + if (gtk_tree_view_is_expander_column (tree_view, column)) + { + if (!rtl) + cell_area.x += (depth - 1) * priv->level_indentation; + cell_area.width -= (depth - 1) * priv->level_indentation; + + if (gtk_tree_view_draw_expanders (tree_view)) + { + int expander_size = gtk_tree_view_get_expander_size (tree_view); + if (!rtl) + cell_area.x += depth * expander_size; + cell_area.width -= depth * expander_size; + } + } + + if (gtk_tree_view_column_cell_is_visible (column)) + { + if (is_separator) + { + GdkRGBA color; + + gtk_style_context_save (context); + gtk_style_context_add_class (context, "separator"); + + gtk_style_context_get_color (context, &color); + gtk_snapshot_append_color (snapshot, + &color, + &GRAPHENE_RECT_INIT( + cell_area.x, + cell_area.y + cell_area.height / 2, + cell_area.x + cell_area.width, + 1 + )); + + gtk_style_context_restore (context); + } + else + { + gtk_tree_view_column_cell_snapshot (column, + snapshot, + &background_area, + &cell_area, + 0, FALSE); + } + } + cell_offset += gtk_tree_view_column_get_width (column); + } + + gtk_treeview_snapshot_border (snapshot, + &GRAPHENE_RECT_INIT(0, 0, bin_window_width + 2, background_area.height + 2)); + + paintable = gtk_snapshot_free_to_paintable (snapshot, NULL); + + return paintable; +} + + +/* + * Interactive search + */ + +/** + * gtk_tree_view_set_enable_search: + * @tree_view: A `GtkTreeView` + * @enable_search: %TRUE, if the user can search interactively + * + * If @enable_search is set, then the user can type in text to search through + * the tree interactively (this is sometimes called "typeahead find"). + * + * Note that even if this is %FALSE, the user can still initiate a search + * using the “start-interactive-search” key binding. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + */ +void +gtk_tree_view_set_enable_search (GtkTreeView *tree_view, + gboolean enable_search) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + enable_search = !!enable_search; + + if (priv->enable_search != enable_search) + { + priv->enable_search = enable_search; + g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_ENABLE_SEARCH]); + } +} + +/** + * gtk_tree_view_get_enable_search: + * @tree_view: A `GtkTreeView` + * + * Returns whether or not the tree allows to start interactive searching + * by typing in text. + * + * Returns: whether or not to let the user search interactively + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + */ +gboolean +gtk_tree_view_get_enable_search (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); + + return priv->enable_search; +} + + +/** + * gtk_tree_view_get_search_column: + * @tree_view: A `GtkTreeView` + * + * Gets the column searched on by the interactive search code. + * + * Returns: the column the interactive search code searches in. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + */ +int +gtk_tree_view_get_search_column (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1); + + return priv->search_column; +} + +/** + * gtk_tree_view_set_search_column: + * @tree_view: A `GtkTreeView` + * @column: the column of the model to search in, or -1 to disable searching + * + * Sets @column as the column where the interactive search code should + * search in for the current model. + * + * If the search column is set, users can use the “start-interactive-search” + * key binding to bring up search popup. The enable-search property controls + * whether simply typing text will also start an interactive search. + * + * Note that @column refers to a column of the current model. The search + * column is reset to -1 when the model is changed. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + */ +void +gtk_tree_view_set_search_column (GtkTreeView *tree_view, + int column) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + g_return_if_fail (column >= -1); + + if (priv->search_column == column) + return; + + priv->search_column = column; + g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_SEARCH_COLUMN]); +} + +/** + * gtk_tree_view_get_search_equal_func: (skip) + * @tree_view: A `GtkTreeView` + * + * Returns the compare function currently in use. + * + * Returns: the currently used compare function for the search code. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + */ + +GtkTreeViewSearchEqualFunc +gtk_tree_view_get_search_equal_func (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL); + + return priv->search_equal_func; +} + +/** + * gtk_tree_view_set_search_equal_func: + * @tree_view: A `GtkTreeView` + * @search_equal_func: the compare function to use during the search + * @search_user_data: (nullable): user data to pass to @search_equal_func + * @search_destroy: (nullable): Destroy notifier for @search_user_data + * + * Sets the compare function for the interactive search capabilities; note + * that somewhat like strcmp() returning 0 for equality + * `GtkTreeView`SearchEqualFunc returns %FALSE on matches. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +void +gtk_tree_view_set_search_equal_func (GtkTreeView *tree_view, + GtkTreeViewSearchEqualFunc search_equal_func, + gpointer search_user_data, + GDestroyNotify search_destroy) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + g_return_if_fail (search_equal_func != NULL); + + if (priv->search_destroy) + priv->search_destroy (priv->search_user_data); + + priv->search_equal_func = search_equal_func; + priv->search_user_data = search_user_data; + priv->search_destroy = search_destroy; + if (priv->search_equal_func == NULL) + priv->search_equal_func = gtk_tree_view_search_equal_func; +} + +/** + * gtk_tree_view_get_search_entry: + * @tree_view: A `GtkTreeView` + * + * Returns the `GtkEntry` which is currently in use as interactive search + * entry for @tree_view. In case the built-in entry is being used, %NULL + * will be returned. + * + * Returns: (transfer none) (nullable): the entry currently in use as search entry. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + */ +GtkEditable * +gtk_tree_view_get_search_entry (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL); + + if (priv->search_custom_entry_set) + return GTK_EDITABLE (priv->search_entry); + + return NULL; +} + +/** + * gtk_tree_view_set_search_entry: + * @tree_view: A `GtkTreeView` + * @entry: (nullable): the entry the interactive search code of @tree_view should use + * + * Sets the entry which the interactive search code will use for this + * @tree_view. This is useful when you want to provide a search entry + * in our interface at all time at a fixed position. Passing %NULL for + * @entry will make the interactive search code use the built-in popup + * entry again. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + */ +void +gtk_tree_view_set_search_entry (GtkTreeView *tree_view, + GtkEditable *entry) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + g_return_if_fail (entry == NULL || GTK_IS_ENTRY (entry) || GTK_IS_SEARCH_ENTRY (entry)); + + if (priv->search_custom_entry_set) + { + if (priv->search_entry_changed_id) + { + g_signal_handler_disconnect (priv->search_entry, + priv->search_entry_changed_id); + priv->search_entry_changed_id = 0; + } + + g_signal_handlers_disconnect_by_func (gtk_entry_get_key_controller (GTK_ENTRY (priv->search_entry)), + G_CALLBACK (gtk_tree_view_search_key_pressed), + tree_view); + + g_object_unref (priv->search_entry); + } + else if (priv->search_popover) + { + gtk_tree_view_destroy_search_popover (tree_view); + } + + if (entry) + { + GtkEventController *controller; + + priv->search_entry = GTK_WIDGET (g_object_ref (entry)); + priv->search_custom_entry_set = TRUE; + + if (priv->search_entry_changed_id == 0) + { + priv->search_entry_changed_id = + g_signal_connect (priv->search_entry, "changed", + G_CALLBACK (gtk_tree_view_search_init), + tree_view); + } + + if (GTK_IS_ENTRY (entry)) + controller = gtk_entry_get_key_controller (GTK_ENTRY (entry)); + else + controller = gtk_search_entry_get_key_controller (GTK_SEARCH_ENTRY (entry)); + g_signal_connect (controller, "key-pressed", + G_CALLBACK (gtk_tree_view_search_key_pressed), tree_view); + + gtk_tree_view_search_init (priv->search_entry, tree_view); + } + else + { + priv->search_entry = NULL; + priv->search_custom_entry_set = FALSE; + } +} + +static void +gtk_tree_view_search_popover_hide (GtkWidget *search_popover, + GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + if (priv->disable_popdown) + return; + + if (priv->search_entry_changed_id) + { + g_signal_handler_disconnect (priv->search_entry, + priv->search_entry_changed_id); + priv->search_entry_changed_id = 0; + } + if (priv->typeselect_flush_timeout) + { + g_source_remove (priv->typeselect_flush_timeout); + priv->typeselect_flush_timeout = 0; + } + + if (gtk_widget_get_visible (search_popover)) + { + gtk_popover_popdown (GTK_POPOVER (search_popover)); + gtk_editable_set_text (GTK_EDITABLE (priv->search_entry), ""); + gtk_widget_grab_focus (GTK_WIDGET (tree_view)); + } +} + +/* Because we're visible but offscreen, we just set a flag in the preedit + * callback. + */ +static void +gtk_tree_view_search_preedit_changed (GtkText *text, + const char *predit, + GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + priv->imcontext_changed = 1; + if (priv->typeselect_flush_timeout) + { + g_source_remove (priv->typeselect_flush_timeout); + priv->typeselect_flush_timeout = + g_timeout_add (GTK_TREE_VIEW_SEARCH_DIALOG_TIMEOUT, + (GSourceFunc) gtk_tree_view_search_entry_flush_timeout, + tree_view); + gdk_source_set_static_name_by_id (priv->typeselect_flush_timeout, "[gtk] gtk_tree_view_search_entry_flush_timeout"); + } + +} + +static void +gtk_tree_view_search_changed (GtkEditable *editable, + GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + priv->imcontext_changed = 1; +} + +static void +gtk_tree_view_search_activate (GtkEntry *entry, + GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreePath *path; + + gtk_tree_view_search_popover_hide (priv->search_popover, tree_view); + + /* If we have a row selected and it's the cursor row, we activate + * the row XXX */ + if (priv->cursor_node && + GTK_TREE_RBNODE_FLAG_SET (priv->cursor_node, GTK_TREE_RBNODE_IS_SELECTED)) + { + path = _gtk_tree_path_new_from_rbtree (priv->cursor_tree, + priv->cursor_node); + + gtk_tree_view_row_activated (tree_view, path, priv->focus_column); + + gtk_tree_path_free (path); + } +} + +static void +gtk_tree_view_search_pressed_cb (GtkGesture *gesture, + int n_press, + double x, + double y, + GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + gtk_tree_view_search_popover_hide (priv->search_popover, tree_view); +} + +static gboolean +gtk_tree_view_search_scroll_event (GtkWidget *widget, + double dx, + double dy, + GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GdkScrollDirection direction; + + direction = dy > 0 ? GDK_SCROLL_DOWN : GDK_SCROLL_UP; + + if (direction == GDK_SCROLL_UP) + gtk_tree_view_search_move (widget, tree_view, TRUE); + else if (direction == GDK_SCROLL_DOWN) + gtk_tree_view_search_move (widget, tree_view, FALSE); + + /* renew the flush timeout */ + if (priv->typeselect_flush_timeout && + !priv->search_custom_entry_set) + { + g_source_remove (priv->typeselect_flush_timeout); + priv->typeselect_flush_timeout = + g_timeout_add (GTK_TREE_VIEW_SEARCH_DIALOG_TIMEOUT, + (GSourceFunc) gtk_tree_view_search_entry_flush_timeout, + tree_view); + gdk_source_set_static_name_by_id (priv->typeselect_flush_timeout, "[gtk] gtk_tree_view_search_entry_flush_timeout"); + } + + return GDK_EVENT_STOP; +} + +static gboolean +gtk_tree_view_search_key_pressed (GtkEventControllerKey *key, + guint keyval, + guint keycode, + GdkModifierType state, + GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkWidget *widget = priv->search_entry; + GdkModifierType default_accel; + gboolean retval = FALSE; + + g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); + + /* close window and cancel the search */ + if (!priv->search_custom_entry_set + && gtk_tree_view_search_key_cancels_search (keyval)) + { + gtk_tree_view_search_popover_hide (priv->search_popover, tree_view); + return TRUE; + } + + default_accel = GDK_CONTROL_MASK; + + /* select previous matching iter */ + if (keyval == GDK_KEY_Up || keyval == GDK_KEY_KP_Up) + { + if (!gtk_tree_view_search_move (widget, tree_view, TRUE)) + gtk_widget_error_bell (widget); + + retval = TRUE; + } + + if (((state & (default_accel | GDK_SHIFT_MASK)) == (default_accel | GDK_SHIFT_MASK)) + && (keyval == GDK_KEY_g || keyval == GDK_KEY_G)) + { + if (!gtk_tree_view_search_move (widget, tree_view, TRUE)) + gtk_widget_error_bell (widget); + + retval = TRUE; + } + + /* select next matching iter */ + if (keyval == GDK_KEY_Down || keyval == GDK_KEY_KP_Down) + { + if (!gtk_tree_view_search_move (widget, tree_view, FALSE)) + gtk_widget_error_bell (widget); + + retval = TRUE; + } + + if (((state & (default_accel | GDK_SHIFT_MASK)) == default_accel) + && (keyval == GDK_KEY_g || keyval == GDK_KEY_G)) + { + if (!gtk_tree_view_search_move (widget, tree_view, FALSE)) + gtk_widget_error_bell (widget); + + retval = TRUE; + } + + /* renew the flush timeout */ + if (retval && priv->typeselect_flush_timeout + && !priv->search_custom_entry_set) + { + g_source_remove (priv->typeselect_flush_timeout); + priv->typeselect_flush_timeout = + g_timeout_add (GTK_TREE_VIEW_SEARCH_DIALOG_TIMEOUT, + (GSourceFunc) gtk_tree_view_search_entry_flush_timeout, + tree_view); + gdk_source_set_static_name_by_id (priv->typeselect_flush_timeout, "[gtk] gtk_tree_view_search_entry_flush_timeout"); + } + + if (!retval) + gtk_event_controller_key_forward (key, priv->search_entry); + + return retval; +} + +/* this function returns FALSE if there is a search string but + * nothing was found, and TRUE otherwise. + */ +static gboolean +gtk_tree_view_search_move (GtkWidget *popover, + GtkTreeView *tree_view, + gboolean up) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + gboolean ret; + int len; + int count = 0; + const char *text; + GtkTreeIter iter; + GtkTreeModel *model; + GtkTreeSelection *selection; + + text = gtk_editable_get_text (GTK_EDITABLE (priv->search_entry)); + + g_return_val_if_fail (text != NULL, FALSE); + + len = strlen (text); + + if (up && priv->selected_iter == 1) + return len < 1; + + if (len < 1) + return TRUE; + + model = gtk_tree_view_get_model (tree_view); + selection = gtk_tree_view_get_selection (tree_view); + + /* search */ + gtk_tree_selection_unselect_all (selection); + if (!gtk_tree_model_get_iter_first (model, &iter)) + return TRUE; + + ret = gtk_tree_view_search_iter (model, selection, &iter, text, + &count, up?((priv->selected_iter) - 1):((priv->selected_iter + 1))); + + if (ret) + { + /* found */ + priv->selected_iter += up?(-1):(1); + return TRUE; + } + else + { + /* return to old iter */ + count = 0; + gtk_tree_model_get_iter_first (model, &iter); + gtk_tree_view_search_iter (model, selection, + &iter, text, + &count, priv->selected_iter); + return FALSE; + } +} + +static gboolean +gtk_tree_view_search_equal_func (GtkTreeModel *model, + int column, + const char *key, + GtkTreeIter *iter, + gpointer search_data) +{ + gboolean retval = TRUE; + const char *str; + char *normalized_string; + char *normalized_key; + char *case_normalized_string = NULL; + char *case_normalized_key = NULL; + GValue value = G_VALUE_INIT; + GValue transformed = G_VALUE_INIT; + + gtk_tree_model_get_value (model, iter, column, &value); + + g_value_init (&transformed, G_TYPE_STRING); + + if (!g_value_transform (&value, &transformed)) + { + g_value_unset (&value); + return TRUE; + } + + g_value_unset (&value); + + str = g_value_get_string (&transformed); + if (!str) + { + g_value_unset (&transformed); + return TRUE; + } + + normalized_string = g_utf8_normalize (str, -1, G_NORMALIZE_ALL); + normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL); + + if (normalized_string && normalized_key) + { + case_normalized_string = g_utf8_casefold (normalized_string, -1); + case_normalized_key = g_utf8_casefold (normalized_key, -1); + + if (strncmp (case_normalized_key, case_normalized_string, strlen (case_normalized_key)) == 0) + retval = FALSE; + } + + g_value_unset (&transformed); + g_free (normalized_key); + g_free (normalized_string); + g_free (case_normalized_key); + g_free (case_normalized_string); + + return retval; +} + +static gboolean +gtk_tree_view_search_iter (GtkTreeModel *model, + GtkTreeSelection *selection, + GtkTreeIter *iter, + const char *text, + int *count, + int n) +{ + GtkTreeRBTree *tree = NULL; + GtkTreeRBNode *node = NULL; + GtkTreePath *path; + + GtkTreeView *tree_view = gtk_tree_selection_get_tree_view (selection); + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + path = gtk_tree_model_get_path (model, iter); + _gtk_tree_view_find_node (tree_view, path, &tree, &node); + + do + { + if (! priv->search_equal_func (model, priv->search_column, text, iter, priv->search_user_data)) + { + (*count)++; + if (*count == n) + { + gtk_tree_view_scroll_to_cell (tree_view, path, NULL, + TRUE, 0.5, 0.0); + gtk_tree_selection_select_iter (selection, iter); + gtk_tree_view_real_set_cursor (tree_view, path, CLAMP_NODE); + + if (path) + gtk_tree_path_free (path); + + return TRUE; + } + } + + if (node->children) + { + gboolean has_child; + GtkTreeIter tmp; + + tree = node->children; + node = gtk_tree_rbtree_first (tree); + + tmp = *iter; + has_child = gtk_tree_model_iter_children (model, iter, &tmp); + gtk_tree_path_down (path); + + /* sanity check */ + TREE_VIEW_INTERNAL_ASSERT (has_child, FALSE); + } + else + { + gboolean done = FALSE; + + do + { + node = gtk_tree_rbtree_next (tree, node); + + if (node) + { + gboolean has_next; + + has_next = gtk_tree_model_iter_next (model, iter); + + done = TRUE; + gtk_tree_path_next (path); + + /* sanity check */ + TREE_VIEW_INTERNAL_ASSERT (has_next, FALSE); + } + else + { + gboolean has_parent; + GtkTreeIter tmp_iter = *iter; + + node = tree->parent_node; + tree = tree->parent_tree; + + if (!tree) + { + if (path) + gtk_tree_path_free (path); + + /* we've run out of tree, done with this func */ + return FALSE; + } + + has_parent = gtk_tree_model_iter_parent (model, + iter, + &tmp_iter); + gtk_tree_path_up (path); + + /* sanity check */ + TREE_VIEW_INTERNAL_ASSERT (has_parent, FALSE); + } + } + while (!done); + } + } + while (1); + + return FALSE; +} + +static void +gtk_tree_view_search_init (GtkWidget *entry, + GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + int ret; + int count = 0; + const char *text; + GtkTreeIter iter; + GtkTreeModel *model; + GtkTreeSelection *selection; + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + text = gtk_editable_get_text (GTK_EDITABLE (entry)); + + model = gtk_tree_view_get_model (tree_view); + selection = gtk_tree_view_get_selection (tree_view); + + /* search */ + gtk_tree_selection_unselect_all (selection); + if (priv->typeselect_flush_timeout + && !priv->search_custom_entry_set) + { + g_source_remove (priv->typeselect_flush_timeout); + priv->typeselect_flush_timeout = + g_timeout_add (GTK_TREE_VIEW_SEARCH_DIALOG_TIMEOUT, + (GSourceFunc) gtk_tree_view_search_entry_flush_timeout, + tree_view); + gdk_source_set_static_name_by_id (priv->typeselect_flush_timeout, "[gtk] gtk_tree_view_search_entry_flush_timeout"); + } + + if (*text == '\0') + return; + + if (!gtk_tree_model_get_iter_first (model, &iter)) + return; + + ret = gtk_tree_view_search_iter (model, selection, + &iter, text, + &count, 1); + + if (ret) + priv->selected_iter = 1; +} + +void +_gtk_tree_view_remove_editable (GtkTreeView *tree_view, + GtkTreeViewColumn *column, + GtkCellEditable *cell_editable) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + if (priv->edited_column == NULL) + return; + + g_return_if_fail (column == priv->edited_column); + + priv->edited_column = NULL; + + if (gtk_widget_has_focus (GTK_WIDGET (cell_editable))) + gtk_widget_grab_focus (GTK_WIDGET (tree_view)); + + gtk_tree_view_remove (tree_view, GTK_WIDGET (cell_editable)); + + /* FIXME should only redraw a single node */ + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); +} + +static gboolean +gtk_tree_view_start_editing (GtkTreeView *tree_view, + GtkTreePath *cursor_path, + gboolean edit_only) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreeIter iter; + GdkRectangle cell_area; + GtkTreeViewColumn *focus_column; + guint flags = 0; /* can be 0, as the flags are primarily for rendering */ + int retval = FALSE; + GtkTreeRBTree *cursor_tree; + GtkTreeRBNode *cursor_node; + + g_assert (priv->focus_column); + focus_column = priv->focus_column; + + if (!gtk_widget_get_realized (GTK_WIDGET (tree_view))) + return FALSE; + + if (_gtk_tree_view_find_node (tree_view, cursor_path, &cursor_tree, &cursor_node) || + cursor_node == NULL) + return FALSE; + + gtk_tree_model_get_iter (priv->model, &iter, cursor_path); + + validate_row (tree_view, cursor_tree, cursor_node, &iter, cursor_path); + + gtk_tree_view_column_cell_set_cell_data (focus_column, + priv->model, + &iter, + GTK_TREE_RBNODE_FLAG_SET (cursor_node, GTK_TREE_RBNODE_IS_PARENT), + cursor_node->children ? TRUE : FALSE); + gtk_tree_view_get_cell_area (tree_view, + cursor_path, + focus_column, + &cell_area); + + if (gtk_cell_area_activate (gtk_cell_layout_get_area (GTK_CELL_LAYOUT (focus_column)), + _gtk_tree_view_column_get_context (focus_column), + GTK_WIDGET (tree_view), + &cell_area, + flags, edit_only)) + retval = TRUE; + + return retval; +} + +void +_gtk_tree_view_add_editable (GtkTreeView *tree_view, + GtkTreeViewColumn *column, + GtkTreePath *path, + GtkCellEditable *cell_editable, + GdkRectangle *cell_area) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GdkRectangle full_area; + GtkBorder border; + + priv->edited_column = column; + + gtk_tree_view_real_set_cursor (tree_view, path, CLAMP_NODE); + + priv->draw_keyfocus = TRUE; + + gtk_tree_view_get_cell_area (tree_view, path, column, &full_area); + border.left = cell_area->x - full_area.x; + border.top = cell_area->y - full_area.y; + border.right = (full_area.x + full_area.width) - (cell_area->x + cell_area->width); + border.bottom = (full_area.y + full_area.height) - (cell_area->y + cell_area->height); + + gtk_tree_view_put (tree_view, + GTK_WIDGET (cell_editable), + path, + column, + &border); +} + +static void +gtk_tree_view_stop_editing (GtkTreeView *tree_view, + gboolean cancel_editing) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreeViewColumn *column; + + if (priv->edited_column == NULL) + return; + + /* + * This is very evil. We need to do this, because + * gtk_cell_editable_editing_done may trigger gtk_tree_view_row_changed + * later on. If gtk_tree_view_row_changed notices + * priv->edited_column != NULL, it'll call + * gtk_tree_view_stop_editing again. Bad things will happen then. + * + * Please read that again if you intend to modify anything here. + */ + + column = priv->edited_column; + gtk_cell_area_stop_editing (gtk_cell_layout_get_area (GTK_CELL_LAYOUT (column)), cancel_editing); + priv->edited_column = NULL; +} + + +/** + * gtk_tree_view_set_hover_selection: + * @tree_view: a `GtkTreeView` + * @hover: %TRUE to enable hover selection mode + * + * Enables or disables the hover selection mode of @tree_view. + * Hover selection makes the selected row follow the pointer. + * Currently, this works only for the selection modes + * %GTK_SELECTION_SINGLE and %GTK_SELECTION_BROWSE. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +void +gtk_tree_view_set_hover_selection (GtkTreeView *tree_view, + gboolean hover) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + hover = hover != FALSE; + + if (hover != priv->hover_selection) + { + priv->hover_selection = hover; + + g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_HOVER_SELECTION]); + } +} + +/** + * gtk_tree_view_get_hover_selection: + * @tree_view: a `GtkTreeView` + * + * Returns whether hover selection mode is turned on for @tree_view. + * + * Returns: %TRUE if @tree_view is in hover selection mode + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +gboolean +gtk_tree_view_get_hover_selection (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); + + return priv->hover_selection; +} + +/** + * gtk_tree_view_set_hover_expand: + * @tree_view: a `GtkTreeView` + * @expand: %TRUE to enable hover selection mode + * + * Enables or disables the hover expansion mode of @tree_view. + * Hover expansion makes rows expand or collapse if the pointer + * moves over them. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +void +gtk_tree_view_set_hover_expand (GtkTreeView *tree_view, + gboolean expand) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + expand = expand != FALSE; + + if (expand != priv->hover_expand) + { + priv->hover_expand = expand; + + g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_HOVER_EXPAND]); + } +} + +/** + * gtk_tree_view_get_hover_expand: + * @tree_view: a `GtkTreeView` + * + * Returns whether hover expansion mode is turned on for @tree_view. + * + * Returns: %TRUE if @tree_view is in hover expansion mode + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +gboolean +gtk_tree_view_get_hover_expand (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); + + return priv->hover_expand; +} + +/** + * gtk_tree_view_set_rubber_banding: + * @tree_view: a `GtkTreeView` + * @enable: %TRUE to enable rubber banding + * + * Enables or disables rubber banding in @tree_view. If the selection mode + * is %GTK_SELECTION_MULTIPLE, rubber banding will allow the user to select + * multiple rows by dragging the mouse. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +void +gtk_tree_view_set_rubber_banding (GtkTreeView *tree_view, + gboolean enable) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + enable = enable != FALSE; + + if (enable != priv->rubber_banding_enable) + { + priv->rubber_banding_enable = enable; + + g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_RUBBER_BANDING]); + } +} + +/** + * gtk_tree_view_get_rubber_banding: + * @tree_view: a `GtkTreeView` + * + * Returns whether rubber banding is turned on for @tree_view. If the + * selection mode is %GTK_SELECTION_MULTIPLE, rubber banding will allow the + * user to select multiple rows by dragging the mouse. + * + * Returns: %TRUE if rubber banding in @tree_view is enabled. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +gboolean +gtk_tree_view_get_rubber_banding (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + return priv->rubber_banding_enable; +} + +/** + * gtk_tree_view_is_rubber_banding_active: + * @tree_view: a `GtkTreeView` + * + * Returns whether a rubber banding operation is currently being done + * in @tree_view. + * + * Returns: %TRUE if a rubber banding operation is currently being + * done in @tree_view. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +gboolean +gtk_tree_view_is_rubber_banding_active (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); + + if (priv->rubber_banding_enable + && priv->rubber_band_status == RUBBER_BAND_ACTIVE) + return TRUE; + + return FALSE; +} + +/** + * gtk_tree_view_get_row_separator_func: (skip) + * @tree_view: a `GtkTreeView` + * + * Returns the current row separator function. + * + * Returns: the current row separator function. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +GtkTreeViewRowSeparatorFunc +gtk_tree_view_get_row_separator_func (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL); + + return priv->row_separator_func; +} + +/** + * gtk_tree_view_set_row_separator_func: + * @tree_view: a `GtkTreeView` + * @func: (nullable): a `GtkTreeView`RowSeparatorFunc + * @data: (nullable): user data to pass to @func + * @destroy: (nullable): destroy notifier for @data + * + * Sets the row separator function, which is used to determine + * whether a row should be drawn as a separator. If the row separator + * function is %NULL, no separators are drawn. This is the default value. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + **/ +void +gtk_tree_view_set_row_separator_func (GtkTreeView *tree_view, + GtkTreeViewRowSeparatorFunc func, + gpointer data, + GDestroyNotify destroy) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + if (priv->row_separator_destroy) + priv->row_separator_destroy (priv->row_separator_data); + + priv->row_separator_func = func; + priv->row_separator_data = data; + priv->row_separator_destroy = destroy; + + /* Have the tree recalculate heights */ + gtk_tree_rbtree_mark_invalid (priv->tree); + gtk_widget_queue_resize (GTK_WIDGET (tree_view)); +} + +/** + * gtk_tree_view_get_grid_lines: + * @tree_view: a `GtkTreeView` + * + * Returns which grid lines are enabled in @tree_view. + * + * Returns: a `GtkTreeView`GridLines value indicating which grid lines + * are enabled. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + */ +GtkTreeViewGridLines +gtk_tree_view_get_grid_lines (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), 0); + + return priv->grid_lines; +} + +/** + * gtk_tree_view_set_grid_lines: + * @tree_view: a `GtkTreeView` + * @grid_lines: a `GtkTreeView`GridLines value indicating which grid lines to + * enable. + * + * Sets which grid lines to draw in @tree_view. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + */ +void +gtk_tree_view_set_grid_lines (GtkTreeView *tree_view, + GtkTreeViewGridLines grid_lines) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GtkTreeViewGridLines old_grid_lines; + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + old_grid_lines = priv->grid_lines; + priv->grid_lines = grid_lines; + + if (old_grid_lines != grid_lines) + { + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + + g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_ENABLE_GRID_LINES]); + } +} + +/** + * gtk_tree_view_get_enable_tree_lines: + * @tree_view: a `GtkTreeView`. + * + * Returns whether or not tree lines are drawn in @tree_view. + * + * Returns: %TRUE if tree lines are drawn in @tree_view, %FALSE + * otherwise. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + */ +gboolean +gtk_tree_view_get_enable_tree_lines (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); + + return priv->tree_lines_enabled; +} + +/** + * gtk_tree_view_set_enable_tree_lines: + * @tree_view: a `GtkTreeView` + * @enabled: %TRUE to enable tree line drawing, %FALSE otherwise. + * + * Sets whether to draw lines interconnecting the expanders in @tree_view. + * This does not have any visible effects for lists. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + */ +void +gtk_tree_view_set_enable_tree_lines (GtkTreeView *tree_view, + gboolean enabled) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + gboolean was_enabled; + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + enabled = enabled != FALSE; + + was_enabled = priv->tree_lines_enabled; + + priv->tree_lines_enabled = enabled; + + if (was_enabled != enabled) + { + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + + g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_ENABLE_TREE_LINES]); + } +} + + +/** + * gtk_tree_view_set_show_expanders: + * @tree_view: a `GtkTreeView` + * @enabled: %TRUE to enable expander drawing, %FALSE otherwise. + * + * Sets whether to draw and enable expanders and indent child rows in + * @tree_view. When disabled there will be no expanders visible in trees + * and there will be no way to expand and collapse rows by default. Also + * note that hiding the expanders will disable the default indentation. You + * can set a custom indentation in this case using + * gtk_tree_view_set_level_indentation(). + * This does not have any visible effects for lists. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + */ +void +gtk_tree_view_set_show_expanders (GtkTreeView *tree_view, + gboolean enabled) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + enabled = enabled != FALSE; + if (priv->show_expanders != enabled) + { + priv->show_expanders = enabled; + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_SHOW_EXPANDERS]); + } +} + +/** + * gtk_tree_view_get_show_expanders: + * @tree_view: a `GtkTreeView`. + * + * Returns whether or not expanders are drawn in @tree_view. + * + * Returns: %TRUE if expanders are drawn in @tree_view, %FALSE + * otherwise. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + */ +gboolean +gtk_tree_view_get_show_expanders (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); + + return priv->show_expanders; +} + +/** + * gtk_tree_view_set_level_indentation: + * @tree_view: a `GtkTreeView` + * @indentation: the amount, in pixels, of extra indentation in @tree_view. + * + * Sets the amount of extra indentation for child levels to use in @tree_view + * in addition to the default indentation. The value should be specified in + * pixels, a value of 0 disables this feature and in this case only the default + * indentation will be used. + * This does not have any visible effects for lists. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + */ +void +gtk_tree_view_set_level_indentation (GtkTreeView *tree_view, + int indentation) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + priv->level_indentation = indentation; + + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); +} + +/** + * gtk_tree_view_get_level_indentation: + * @tree_view: a `GtkTreeView`. + * + * Returns the amount, in pixels, of extra indentation for child levels + * in @tree_view. + * + * Returns: the amount of extra indentation for child levels in + * @tree_view. A return value of 0 means that this feature is disabled. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + */ +int +gtk_tree_view_get_level_indentation (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), 0); + + return priv->level_indentation; +} + +/** + * gtk_tree_view_set_tooltip_row: + * @tree_view: a `GtkTreeView` + * @tooltip: a `GtkTooltip` + * @path: a `GtkTreePath` + * + * Sets the tip area of @tooltip to be the area covered by the row at @path. + * See also gtk_tree_view_set_tooltip_column() for a simpler alternative. + * See also gtk_tooltip_set_tip_area(). + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + */ +void +gtk_tree_view_set_tooltip_row (GtkTreeView *tree_view, + GtkTooltip *tooltip, + GtkTreePath *path) +{ + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + g_return_if_fail (GTK_IS_TOOLTIP (tooltip)); + + gtk_tree_view_set_tooltip_cell (tree_view, tooltip, path, NULL, NULL); +} + +/** + * gtk_tree_view_set_tooltip_cell: + * @tree_view: a `GtkTreeView` + * @tooltip: a `GtkTooltip` + * @path: (nullable): a `GtkTreePath` + * @column: (nullable): a `GtkTreeViewColumn` + * @cell: (nullable): a `GtkCellRenderer` + * + * Sets the tip area of @tooltip to the area @path, @column and @cell have + * in common. For example if @path is %NULL and @column is set, the tip + * area will be set to the full area covered by @column. See also + * gtk_tooltip_set_tip_area(). + * + * Note that if @path is not specified and @cell is set and part of a column + * containing the expander, the tooltip might not show and hide at the correct + * position. In such cases @path must be set to the current node under the + * mouse cursor for this function to operate correctly. + * + * See also gtk_tree_view_set_tooltip_column() for a simpler alternative. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + */ +void +gtk_tree_view_set_tooltip_cell (GtkTreeView *tree_view, + GtkTooltip *tooltip, + GtkTreePath *path, + GtkTreeViewColumn *column, + GtkCellRenderer *cell) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + GdkRectangle rect; + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + g_return_if_fail (GTK_IS_TOOLTIP (tooltip)); + g_return_if_fail (column == NULL || GTK_IS_TREE_VIEW_COLUMN (column)); + g_return_if_fail (cell == NULL || GTK_IS_CELL_RENDERER (cell)); + + /* Determine x values. */ + if (column && cell) + { + GdkRectangle tmp; + int start, width; + + /* We always pass in path here, whether it is NULL or not. + * For cells in expander columns path must be specified so that + * we can correctly account for the indentation. This also means + * that the tooltip is constrained vertically by the "Determine y + * values" code below; this is not a real problem since cells actually + * don't stretch vertically in contrast to columns. + */ + gtk_tree_view_get_cell_area (tree_view, path, column, &tmp); + gtk_tree_view_column_cell_get_position (column, cell, &start, &width); + + gtk_tree_view_convert_bin_window_to_widget_coords (tree_view, + tmp.x + start, 0, + &rect.x, NULL); + rect.width = width; + } + else if (column) + { + GdkRectangle tmp; + + gtk_tree_view_get_background_area (tree_view, NULL, column, &tmp); + gtk_tree_view_convert_bin_window_to_widget_coords (tree_view, + tmp.x, 0, + &rect.x, NULL); + rect.width = tmp.width; + } + else + { + rect.x = 0; + rect.width = gtk_widget_get_width (GTK_WIDGET (tree_view));; + } + + /* Determine y values. */ + if (path) + { + GdkRectangle tmp; + + gtk_tree_view_get_background_area (tree_view, path, NULL, &tmp); + gtk_tree_view_convert_bin_window_to_widget_coords (tree_view, + 0, tmp.y, + NULL, &rect.y); + rect.height = tmp.height; + } + else + { + rect.y = 0; + rect.height = gtk_adjustment_get_page_size (priv->vadjustment); + } + + gtk_tooltip_set_tip_area (tooltip, &rect); +} + +/** + * gtk_tree_view_get_tooltip_context: + * @tree_view: a `GtkTreeView` + * @x: the x coordinate (relative to widget coordinates) + * @y: the y coordinate (relative to widget coordinates) + * @keyboard_tip: whether this is a keyboard tooltip or not + * @model: (out) (optional) (nullable) (transfer none): a pointer to + * receive a `GtkTreeModel` + * @path: (out) (optional): a pointer to receive a `GtkTreePath` + * @iter: (out) (optional): a pointer to receive a `GtkTreeIter` + * + * This function is supposed to be used in a ::query-tooltip + * signal handler for `GtkTreeView`. The @x, @y and @keyboard_tip values + * which are received in the signal handler, should be passed to this + * function without modification. + * + * The return value indicates whether there is a tree view row at the given + * coordinates (%TRUE) or not (%FALSE) for mouse tooltips. For keyboard + * tooltips the row returned will be the cursor row. When %TRUE, then any of + * @model, @path and @iter which have been provided will be set to point to + * that row and the corresponding model. @x and @y will always be converted + * to be relative to @tree_view’s bin_window if @keyboard_tooltip is %FALSE. + * + * Returns: whether or not the given tooltip context points to a row + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + */ +gboolean +gtk_tree_view_get_tooltip_context (GtkTreeView *tree_view, + int x, + int y, + gboolean keyboard_tip, + GtkTreeModel **model, + GtkTreePath **path, + GtkTreeIter *iter) +{ + GtkTreePath *tmppath = NULL; + + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); + + if (keyboard_tip) + { + gtk_tree_view_get_cursor (tree_view, &tmppath, NULL); + + if (!tmppath) + return FALSE; + } + else + { + int rel_x, rel_y; + + gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, x, y, + &rel_x, &rel_y); + + if (!gtk_tree_view_get_path_at_pos (tree_view, rel_x, rel_y, + &tmppath, NULL, NULL, NULL)) + return FALSE; + } + + if (model) + *model = gtk_tree_view_get_model (tree_view); + + if (iter) + gtk_tree_model_get_iter (gtk_tree_view_get_model (tree_view), + iter, tmppath); + + if (path) + *path = tmppath; + else + gtk_tree_path_free (tmppath); + + return TRUE; +} + +static gboolean +gtk_tree_view_set_tooltip_query_cb (GtkWidget *widget, + int x, + int y, + gboolean keyboard_tip, + GtkTooltip *tooltip, + gpointer data) +{ + GValue value = G_VALUE_INIT; + GValue transformed = G_VALUE_INIT; + GtkTreeIter iter; + GtkTreePath *path; + GtkTreeModel *model; + GtkTreeView *tree_view = GTK_TREE_VIEW (widget); + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + if (!gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (widget), + x, y, + keyboard_tip, + &model, &path, &iter)) + return FALSE; + + gtk_tree_model_get_value (model, &iter, + priv->tooltip_column, &value); + + g_value_init (&transformed, G_TYPE_STRING); + + if (!g_value_transform (&value, &transformed)) + { + g_value_unset (&value); + gtk_tree_path_free (path); + + return FALSE; + } + + g_value_unset (&value); + + if (!g_value_get_string (&transformed)) + { + g_value_unset (&transformed); + gtk_tree_path_free (path); + + return FALSE; + } + + gtk_tooltip_set_markup (tooltip, g_value_get_string (&transformed)); + gtk_tree_view_set_tooltip_row (tree_view, tooltip, path); + + gtk_tree_path_free (path); + g_value_unset (&transformed); + + return TRUE; +} + +/** + * gtk_tree_view_set_tooltip_column: + * @tree_view: a `GtkTreeView` + * @column: an integer, which is a valid column number for @tree_view’s model + * + * If you only plan to have simple (text-only) tooltips on full rows, you + * can use this function to have `GtkTreeView` handle these automatically + * for you. @column should be set to the column in @tree_view’s model + * containing the tooltip texts, or -1 to disable this feature. + * + * When enabled, `GtkWidget:has-tooltip` will be set to %TRUE and + * @tree_view will connect a `GtkWidget::query-tooltip` signal handler. + * + * Note that the signal handler sets the text with gtk_tooltip_set_markup(), + * so &, <, etc have to be escaped in the text. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + */ +void +gtk_tree_view_set_tooltip_column (GtkTreeView *tree_view, + int column) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + if (column == priv->tooltip_column) + return; + + if (column == -1) + { + g_signal_handlers_disconnect_by_func (tree_view, + gtk_tree_view_set_tooltip_query_cb, + NULL); + gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), FALSE); + } + else + { + if (priv->tooltip_column == -1) + { + g_signal_connect (tree_view, "query-tooltip", + G_CALLBACK (gtk_tree_view_set_tooltip_query_cb), NULL); + gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), TRUE); + } + } + + priv->tooltip_column = column; + g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_TOOLTIP_COLUMN]); +} + +/** + * gtk_tree_view_get_tooltip_column: + * @tree_view: a `GtkTreeView` + * + * Returns the column of @tree_view’s model which is being used for + * displaying tooltips on @tree_view’s rows. + * + * Returns: the index of the tooltip column that is currently being + * used, or -1 if this is disabled. + * + * Deprecated: 4.12: Use GtkListView and GtkColumnView instead + */ +int +gtk_tree_view_get_tooltip_column (GtkTreeView *tree_view) +{ + GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); + + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), 0); + + return priv->tooltip_column; +} + +static gboolean +gtk_tree_view_get_border (GtkScrollable *scrollable, + GtkBorder *border) +{ + border->top = gtk_tree_view_get_effective_header_height (GTK_TREE_VIEW (scrollable)); + + return TRUE; +} + +static void +gtk_tree_view_scrollable_init (GtkScrollableInterface *iface) +{ + iface->get_border = gtk_tree_view_get_border; +} diff --git a/gtk/deprecated/gtktreeview.h b/gtk/deprecated/gtktreeview.h new file mode 100644 index 0000000000..f4c020d923 --- /dev/null +++ b/gtk/deprecated/gtktreeview.h @@ -0,0 +1,548 @@ +/* gtktreeview.h + * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#ifndef __GTK_TREE_VIEW_H__ +#define __GTK_TREE_VIEW_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include +#include +#include + +G_BEGIN_DECLS + +/** + * GtkTreeViewDropPosition: + * @GTK_TREE_VIEW_DROP_BEFORE: dropped row is inserted before + * @GTK_TREE_VIEW_DROP_AFTER: dropped row is inserted after + * @GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: dropped row becomes a child or is inserted before + * @GTK_TREE_VIEW_DROP_INTO_OR_AFTER: dropped row becomes a child or is inserted after + * + * An enum for determining where a dropped row goes. + */ +typedef enum +{ + /* drop before/after this row */ + GTK_TREE_VIEW_DROP_BEFORE, + GTK_TREE_VIEW_DROP_AFTER, + /* drop as a child of this row (with fallback to before or after + * if into is not possible) + */ + GTK_TREE_VIEW_DROP_INTO_OR_BEFORE, + GTK_TREE_VIEW_DROP_INTO_OR_AFTER +} GtkTreeViewDropPosition; + +#define GTK_TYPE_TREE_VIEW (gtk_tree_view_get_type ()) +#define GTK_TREE_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_TREE_VIEW, GtkTreeView)) +#define GTK_IS_TREE_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_TREE_VIEW)) +#define GTK_TREE_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_TREE_VIEW, GtkTreeViewClass)) +#define GTK_IS_TREE_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_TREE_VIEW)) +#define GTK_TREE_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_TREE_VIEW, GtkTreeViewClass)) + +typedef struct _GtkTreeView GtkTreeView; +typedef struct _GtkTreeViewClass GtkTreeViewClass; +typedef struct _GtkTreeSelection GtkTreeSelection; + +/** + * GtkTreeViewColumnDropFunc: + * @tree_view: A `GtkTreeView` + * @column: The `GtkTreeViewColumn` being dragged + * @prev_column: A `GtkTreeViewColumn` on one side of @column + * @next_column: A `GtkTreeViewColumn` on the other side of @column + * @data: (closure): user data + * + * Function type for determining whether @column can be dropped in a + * particular spot (as determined by @prev_column and @next_column). In + * left to right locales, @prev_column is on the left of the potential drop + * spot, and @next_column is on the right. In right to left mode, this is + * reversed. This function should return %TRUE if the spot is a valid drop + * spot. Please note that returning %TRUE does not actually indicate that + * the column drop was made, but is meant only to indicate a possible drop + * spot to the user. + * + * Returns: %TRUE, if @column can be dropped in this spot + */ +typedef gboolean (* GtkTreeViewColumnDropFunc) (GtkTreeView *tree_view, + GtkTreeViewColumn *column, + GtkTreeViewColumn *prev_column, + GtkTreeViewColumn *next_column, + gpointer data); + +/** + * GtkTreeViewMappingFunc: + * @tree_view: A `GtkTreeView` + * @path: The path that’s expanded + * @user_data: user data + * + * Function used for gtk_tree_view_map_expanded_rows(). + */ +typedef void (* GtkTreeViewMappingFunc) (GtkTreeView *tree_view, + GtkTreePath *path, + gpointer user_data); + +/** + * GtkTreeViewSearchEqualFunc: + * @model: the `GtkTreeModel` being searched + * @column: the search column set by gtk_tree_view_set_search_column() + * @key: the key string to compare with + * @iter: a `GtkTreeIter` pointing the row of @model that should be compared + * with @key. + * @search_data: (closure): user data from gtk_tree_view_set_search_equal_func() + * + * A function used for checking whether a row in @model matches + * a search key string entered by the user. Note the return value + * is reversed from what you would normally expect, though it + * has some similarity to strcmp() returning 0 for equal strings. + * + * Returns: %FALSE if the row matches, %TRUE otherwise. + */ +typedef gboolean (*GtkTreeViewSearchEqualFunc) (GtkTreeModel *model, + int column, + const char *key, + GtkTreeIter *iter, + gpointer search_data); + +/** + * GtkTreeViewRowSeparatorFunc: + * @model: the `GtkTreeModel` + * @iter: a `GtkTreeIter` pointing at a row in @model + * @data: (closure): user data + * + * Function type for determining whether the row pointed to by @iter should + * be rendered as a separator. A common way to implement this is to have a + * boolean column in the model, whose values the `GtkTreeViewRowSeparatorFunc` + * returns. + * + * Returns: %TRUE if the row is a separator + */ +typedef gboolean (*GtkTreeViewRowSeparatorFunc) (GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data); + +struct _GtkTreeView +{ + GtkWidget parent_instance; +}; + +struct _GtkTreeViewClass +{ + GtkWidgetClass parent_class; + + void (* row_activated) (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column); + gboolean (* test_expand_row) (GtkTreeView *tree_view, + GtkTreeIter *iter, + GtkTreePath *path); + gboolean (* test_collapse_row) (GtkTreeView *tree_view, + GtkTreeIter *iter, + GtkTreePath *path); + void (* row_expanded) (GtkTreeView *tree_view, + GtkTreeIter *iter, + GtkTreePath *path); + void (* row_collapsed) (GtkTreeView *tree_view, + GtkTreeIter *iter, + GtkTreePath *path); + void (* columns_changed) (GtkTreeView *tree_view); + void (* cursor_changed) (GtkTreeView *tree_view); + + /* Key Binding signals */ + gboolean (* move_cursor) (GtkTreeView *tree_view, + GtkMovementStep step, + int count, + gboolean extend, + gboolean modify); + gboolean (* select_all) (GtkTreeView *tree_view); + gboolean (* unselect_all) (GtkTreeView *tree_view); + gboolean (* select_cursor_row) (GtkTreeView *tree_view, + gboolean start_editing); + gboolean (* toggle_cursor_row) (GtkTreeView *tree_view); + gboolean (* expand_collapse_cursor_row) (GtkTreeView *tree_view, + gboolean logical, + gboolean expand, + gboolean open_all); + gboolean (* select_cursor_parent) (GtkTreeView *tree_view); + gboolean (* start_interactive_search) (GtkTreeView *tree_view); + + /*< private >*/ + gpointer _reserved[16]; +}; + +GDK_AVAILABLE_IN_ALL +GType gtk_tree_view_get_type (void) G_GNUC_CONST; + +/* Creators */ +GDK_DEPRECATED_IN_4_10 +GtkWidget *gtk_tree_view_new (void); +GDK_DEPRECATED_IN_4_10 +GtkWidget *gtk_tree_view_new_with_model (GtkTreeModel *model); + +/* Accessors */ +GDK_DEPRECATED_IN_4_10 +GtkTreeModel *gtk_tree_view_get_model (GtkTreeView *tree_view); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_set_model (GtkTreeView *tree_view, + GtkTreeModel *model); +GDK_DEPRECATED_IN_4_10 +GtkTreeSelection *gtk_tree_view_get_selection (GtkTreeView *tree_view); + +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_view_get_headers_visible (GtkTreeView *tree_view); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_set_headers_visible (GtkTreeView *tree_view, + gboolean headers_visible); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_columns_autosize (GtkTreeView *tree_view); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_view_get_headers_clickable (GtkTreeView *tree_view); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_set_headers_clickable (GtkTreeView *tree_view, + gboolean setting); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_view_get_activate_on_single_click (GtkTreeView *tree_view); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_set_activate_on_single_click (GtkTreeView *tree_view, + gboolean single); + +/* Column functions */ +GDK_DEPRECATED_IN_4_10 +int gtk_tree_view_append_column (GtkTreeView *tree_view, + GtkTreeViewColumn *column); +GDK_DEPRECATED_IN_4_10 +int gtk_tree_view_remove_column (GtkTreeView *tree_view, + GtkTreeViewColumn *column); +GDK_DEPRECATED_IN_4_10 +int gtk_tree_view_insert_column (GtkTreeView *tree_view, + GtkTreeViewColumn *column, + int position); +GDK_DEPRECATED_IN_4_10 +int gtk_tree_view_insert_column_with_attributes (GtkTreeView *tree_view, + int position, + const char *title, + GtkCellRenderer *cell, + ...) G_GNUC_NULL_TERMINATED; +GDK_DEPRECATED_IN_4_10 +int gtk_tree_view_insert_column_with_data_func (GtkTreeView *tree_view, + int position, + const char *title, + GtkCellRenderer *cell, + GtkTreeCellDataFunc func, + gpointer data, + GDestroyNotify dnotify); + +GDK_DEPRECATED_IN_4_10 +guint gtk_tree_view_get_n_columns (GtkTreeView *tree_view); +GDK_DEPRECATED_IN_4_10 +GtkTreeViewColumn *gtk_tree_view_get_column (GtkTreeView *tree_view, + int n); +GDK_DEPRECATED_IN_4_10 +GList *gtk_tree_view_get_columns (GtkTreeView *tree_view); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_move_column_after (GtkTreeView *tree_view, + GtkTreeViewColumn *column, + GtkTreeViewColumn *base_column); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_set_expander_column (GtkTreeView *tree_view, + GtkTreeViewColumn *column); +GDK_DEPRECATED_IN_4_10 +GtkTreeViewColumn *gtk_tree_view_get_expander_column (GtkTreeView *tree_view); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_set_column_drag_function (GtkTreeView *tree_view, + GtkTreeViewColumnDropFunc func, + gpointer user_data, + GDestroyNotify destroy); + +/* Actions */ +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_scroll_to_point (GtkTreeView *tree_view, + int tree_x, + int tree_y); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_scroll_to_cell (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + gboolean use_align, + float row_align, + float col_align); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_row_activated (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_expand_all (GtkTreeView *tree_view); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_collapse_all (GtkTreeView *tree_view); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_expand_to_path (GtkTreeView *tree_view, + GtkTreePath *path); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_view_expand_row (GtkTreeView *tree_view, + GtkTreePath *path, + gboolean open_all); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_view_collapse_row (GtkTreeView *tree_view, + GtkTreePath *path); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_map_expanded_rows (GtkTreeView *tree_view, + GtkTreeViewMappingFunc func, + gpointer data); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_view_row_expanded (GtkTreeView *tree_view, + GtkTreePath *path); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_set_reorderable (GtkTreeView *tree_view, + gboolean reorderable); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_view_get_reorderable (GtkTreeView *tree_view); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_set_cursor (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *focus_column, + gboolean start_editing); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_set_cursor_on_cell (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *focus_column, + GtkCellRenderer *focus_cell, + gboolean start_editing); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_get_cursor (GtkTreeView *tree_view, + GtkTreePath **path, + GtkTreeViewColumn **focus_column); + + +/* Layout information */ +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_view_get_path_at_pos (GtkTreeView *tree_view, + int x, + int y, + GtkTreePath **path, + GtkTreeViewColumn **column, + int *cell_x, + int *cell_y); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_get_cell_area (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + GdkRectangle *rect); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_get_background_area (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + GdkRectangle *rect); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_get_visible_rect (GtkTreeView *tree_view, + GdkRectangle *visible_rect); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_view_get_visible_range (GtkTreeView *tree_view, + GtkTreePath **start_path, + GtkTreePath **end_path); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_view_is_blank_at_pos (GtkTreeView *tree_view, + int x, + int y, + GtkTreePath **path, + GtkTreeViewColumn **column, + int *cell_x, + int *cell_y); + +/* Drag-and-Drop support */ +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_enable_model_drag_source (GtkTreeView *tree_view, + GdkModifierType start_button_mask, + GdkContentFormats *formats, + GdkDragAction actions); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_enable_model_drag_dest (GtkTreeView *tree_view, + GdkContentFormats *formats, + GdkDragAction actions); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_unset_rows_drag_source (GtkTreeView *tree_view); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_unset_rows_drag_dest (GtkTreeView *tree_view); + + +/* These are useful to implement your own custom stuff. */ +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_set_drag_dest_row (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewDropPosition pos); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_get_drag_dest_row (GtkTreeView *tree_view, + GtkTreePath **path, + GtkTreeViewDropPosition *pos); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_view_get_dest_row_at_pos (GtkTreeView *tree_view, + int drag_x, + int drag_y, + GtkTreePath **path, + GtkTreeViewDropPosition *pos); +GDK_DEPRECATED_IN_4_10 +GdkPaintable *gtk_tree_view_create_row_drag_icon (GtkTreeView *tree_view, + GtkTreePath *path); + +/* Interactive search */ +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_set_enable_search (GtkTreeView *tree_view, + gboolean enable_search); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_view_get_enable_search (GtkTreeView *tree_view); +GDK_DEPRECATED_IN_4_10 +int gtk_tree_view_get_search_column (GtkTreeView *tree_view); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_set_search_column (GtkTreeView *tree_view, + int column); +GDK_DEPRECATED_IN_4_10 +GtkTreeViewSearchEqualFunc gtk_tree_view_get_search_equal_func (GtkTreeView *tree_view); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_set_search_equal_func (GtkTreeView *tree_view, + GtkTreeViewSearchEqualFunc search_equal_func, + gpointer search_user_data, + GDestroyNotify search_destroy); + +GDK_DEPRECATED_IN_4_10 +GtkEditable *gtk_tree_view_get_search_entry (GtkTreeView *tree_view); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_set_search_entry (GtkTreeView *tree_view, + GtkEditable *entry); + +/* Convert between the different coordinate systems */ +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_convert_widget_to_tree_coords (GtkTreeView *tree_view, + int wx, + int wy, + int *tx, + int *ty); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_convert_tree_to_widget_coords (GtkTreeView *tree_view, + int tx, + int ty, + int *wx, + int *wy); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_convert_widget_to_bin_window_coords (GtkTreeView *tree_view, + int wx, + int wy, + int *bx, + int *by); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_convert_bin_window_to_widget_coords (GtkTreeView *tree_view, + int bx, + int by, + int *wx, + int *wy); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_convert_tree_to_bin_window_coords (GtkTreeView *tree_view, + int tx, + int ty, + int *bx, + int *by); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_convert_bin_window_to_tree_coords (GtkTreeView *tree_view, + int bx, + int by, + int *tx, + int *ty); + +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_set_fixed_height_mode (GtkTreeView *tree_view, + gboolean enable); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_view_get_fixed_height_mode (GtkTreeView *tree_view); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_set_hover_selection (GtkTreeView *tree_view, + gboolean hover); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_view_get_hover_selection (GtkTreeView *tree_view); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_set_hover_expand (GtkTreeView *tree_view, + gboolean expand); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_view_get_hover_expand (GtkTreeView *tree_view); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_set_rubber_banding (GtkTreeView *tree_view, + gboolean enable); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_view_get_rubber_banding (GtkTreeView *tree_view); + +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_view_is_rubber_banding_active (GtkTreeView *tree_view); + +GDK_DEPRECATED_IN_4_10 +GtkTreeViewRowSeparatorFunc gtk_tree_view_get_row_separator_func (GtkTreeView *tree_view); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_set_row_separator_func (GtkTreeView *tree_view, + GtkTreeViewRowSeparatorFunc func, + gpointer data, + GDestroyNotify destroy); + +GDK_DEPRECATED_IN_4_10 +GtkTreeViewGridLines gtk_tree_view_get_grid_lines (GtkTreeView *tree_view); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_set_grid_lines (GtkTreeView *tree_view, + GtkTreeViewGridLines grid_lines); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_view_get_enable_tree_lines (GtkTreeView *tree_view); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_set_enable_tree_lines (GtkTreeView *tree_view, + gboolean enabled); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_set_show_expanders (GtkTreeView *tree_view, + gboolean enabled); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_view_get_show_expanders (GtkTreeView *tree_view); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_set_level_indentation (GtkTreeView *tree_view, + int indentation); +GDK_DEPRECATED_IN_4_10 +int gtk_tree_view_get_level_indentation (GtkTreeView *tree_view); + +/* Convenience functions for setting tooltips */ +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_set_tooltip_row (GtkTreeView *tree_view, + GtkTooltip *tooltip, + GtkTreePath *path); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_set_tooltip_cell (GtkTreeView *tree_view, + GtkTooltip *tooltip, + GtkTreePath *path, + GtkTreeViewColumn *column, + GtkCellRenderer *cell); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_view_get_tooltip_context(GtkTreeView *tree_view, + int x, + int y, + gboolean keyboard_tip, + GtkTreeModel **model, + GtkTreePath **path, + GtkTreeIter *iter); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_set_tooltip_column (GtkTreeView *tree_view, + int column); +GDK_DEPRECATED_IN_4_10 +int gtk_tree_view_get_tooltip_column (GtkTreeView *tree_view); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkTreeView, g_object_unref) + +G_END_DECLS + + +#endif /* __GTK_TREE_VIEW_H__ */ diff --git a/gtk/deprecated/gtktreeviewcolumn.c b/gtk/deprecated/gtktreeviewcolumn.c new file mode 100644 index 0000000000..fc45a65bdb --- /dev/null +++ b/gtk/deprecated/gtktreeviewcolumn.c @@ -0,0 +1,3160 @@ +/* gtktreeviewcolumn.c + * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#include "config.h" + +#include "gtktreeviewcolumn.h" + +#include "gtkbox.h" +#include "gtkbutton.h" +#include "gtkcellareabox.h" +#include "gtkcellareacontext.h" +#include "gtkcelllayout.h" +#include "gtkdragsourceprivate.h" +#include "gtkframe.h" +#include "gtkimage.h" +#include "gtklabel.h" +#include "gtkmarshalers.h" +#include "gtkprivate.h" +#include "gtktreeprivate.h" +#include "deprecated/gtktreeview.h" +#include "gtktypebuiltins.h" +#include "gtkwidgetprivate.h" +#include "gtkgesturedrag.h" +#include "gtkeventcontrollerfocus.h" +#include "gtkeventcontrollerkey.h" +#include "gtkbuiltiniconprivate.h" + +#include + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + +/** + * GtkTreeViewColumn: + * + * A visible column in a [class@Gtk.TreeView] widget + * + * The `GtkTreeViewColumn` object represents a visible column in a `GtkTreeView` widget. + * It allows to set properties of the column header, and functions as a holding pen + * for the cell renderers which determine how the data in the column is displayed. + * + * Please refer to the [tree widget conceptual overview](section-tree-widget.html) + * for an overview of all the objects and data types related to the tree widget and + * how they work together, and to the [class@Gtk.TreeView] documentation for specifics + * about the CSS node structure for treeviews and their headers. + */ + + +/* Type methods */ +static void gtk_tree_view_column_cell_layout_init (GtkCellLayoutIface *iface); + +/* GObject methods */ +static void gtk_tree_view_column_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gtk_tree_view_column_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static void gtk_tree_view_column_finalize (GObject *object); +static void gtk_tree_view_column_dispose (GObject *object); +static void gtk_tree_view_column_constructed (GObject *object); + +/* GtkCellLayout implementation */ +static void gtk_tree_view_column_ensure_cell_area (GtkTreeViewColumn *column, + GtkCellArea *cell_area); + +static GtkCellArea *gtk_tree_view_column_cell_layout_get_area (GtkCellLayout *cell_layout); + +/* Button handling code */ +static void gtk_tree_view_column_create_button (GtkTreeViewColumn *tree_column); +static void gtk_tree_view_column_update_button (GtkTreeViewColumn *tree_column); + +/* Button signal handlers */ +static void column_button_drag_begin (GtkGestureDrag *gesture, + double x, + double y, + GtkTreeViewColumn *column); +static void column_button_drag_update (GtkGestureDrag *gesture, + double offset_x, + double offset_y, + GtkTreeViewColumn *column); + +static void gtk_tree_view_column_button_clicked (GtkWidget *widget, + gpointer data); +static gboolean gtk_tree_view_column_mnemonic_activate (GtkWidget *widget, + gboolean group_cycling, + gpointer data); + +/* Property handlers */ +static void gtk_tree_view_model_sort_column_changed (GtkTreeSortable *sortable, + GtkTreeViewColumn *tree_column); + +/* GtkCellArea/GtkCellAreaContext callbacks */ +static void gtk_tree_view_column_context_changed (GtkCellAreaContext *context, + GParamSpec *pspec, + GtkTreeViewColumn *tree_column); +static void gtk_tree_view_column_add_editable_callback (GtkCellArea *area, + GtkCellRenderer *renderer, + GtkCellEditable *edit_widget, + GdkRectangle *cell_area, + const char *path_string, + gpointer user_data); +static void gtk_tree_view_column_remove_editable_callback (GtkCellArea *area, + GtkCellRenderer *renderer, + GtkCellEditable *edit_widget, + gpointer user_data); + +/* Internal functions */ +static void gtk_tree_view_column_sort (GtkTreeViewColumn *tree_column, + gpointer data); +static void gtk_tree_view_column_setup_sort_column_id_callback (GtkTreeViewColumn *tree_column); +static void gtk_tree_view_column_set_attributesv (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell_renderer, + va_list args); + +/* GtkBuildable implementation */ +static void gtk_tree_view_column_buildable_init (GtkBuildableIface *iface); + +typedef struct _GtkTreeViewColumnClass GtkTreeViewColumnClass; +typedef struct _GtkTreeViewColumnPrivate GtkTreeViewColumnPrivate; + +struct _GtkTreeViewColumn +{ + GInitiallyUnowned parent_instance; + + GtkTreeViewColumnPrivate *priv; +}; + +struct _GtkTreeViewColumnClass +{ + GInitiallyUnownedClass parent_class; + + void (*clicked) (GtkTreeViewColumn *tree_column); +}; + + +struct _GtkTreeViewColumnPrivate +{ + GtkWidget *tree_view; + GtkWidget *button; + GtkWidget *child; + GtkWidget *arrow; + GtkWidget *frame; + gulong property_changed_signal; + float xalign; + + /* Sizing fields */ + /* see gtk+/doc/tree-column-sizing.txt for more information on them */ + GtkTreeViewColumnSizing column_type; + int padding; + int x_offset; + int width; + int fixed_width; + int min_width; + int max_width; + + /* dragging columns */ + int drag_x; + int drag_y; + + char *title; + + /* Sorting */ + gulong sort_clicked_signal; + gulong sort_column_changed_signal; + int sort_column_id; + GtkSortType sort_order; + + /* Cell area */ + GtkCellArea *cell_area; + GtkCellAreaContext *cell_area_context; + gulong add_editable_signal; + gulong remove_editable_signal; + gulong context_changed_signal; + + /* Flags */ + guint visible : 1; + guint resizable : 1; + guint clickable : 1; + guint dirty : 1; + guint show_sort_indicator : 1; + guint maybe_reordered : 1; + guint reorderable : 1; + guint expand : 1; +}; + +enum +{ + PROP_0, + PROP_VISIBLE, + PROP_RESIZABLE, + PROP_X_OFFSET, + PROP_WIDTH, + PROP_SPACING, + PROP_SIZING, + PROP_FIXED_WIDTH, + PROP_MIN_WIDTH, + PROP_MAX_WIDTH, + PROP_TITLE, + PROP_EXPAND, + PROP_CLICKABLE, + PROP_WIDGET, + PROP_ALIGNMENT, + PROP_REORDERABLE, + PROP_SORT_INDICATOR, + PROP_SORT_ORDER, + PROP_SORT_COLUMN_ID, + PROP_CELL_AREA, + LAST_PROP +}; + +enum +{ + CLICKED, + LAST_SIGNAL +}; + +static guint tree_column_signals[LAST_SIGNAL] = { 0 }; +static GParamSpec *tree_column_props[LAST_PROP] = { NULL, }; + +G_DEFINE_TYPE_WITH_CODE (GtkTreeViewColumn, gtk_tree_view_column, G_TYPE_INITIALLY_UNOWNED, + G_ADD_PRIVATE (GtkTreeViewColumn) + G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT, + gtk_tree_view_column_cell_layout_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, + gtk_tree_view_column_buildable_init)) + + +static void +gtk_tree_view_column_class_init (GtkTreeViewColumnClass *class) +{ + GObjectClass *object_class; + + object_class = (GObjectClass*) class; + + class->clicked = NULL; + + object_class->constructed = gtk_tree_view_column_constructed; + object_class->finalize = gtk_tree_view_column_finalize; + object_class->dispose = gtk_tree_view_column_dispose; + object_class->set_property = gtk_tree_view_column_set_property; + object_class->get_property = gtk_tree_view_column_get_property; + + /** + * GtkTreeViewColumn::clicked: + * @column: the `GtkTreeViewColumn` that emitted the signal + * + * Emitted when the column's header has been clicked. + */ + tree_column_signals[CLICKED] = + g_signal_new (I_("clicked"), + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkTreeViewColumnClass, clicked), + NULL, NULL, + NULL, + G_TYPE_NONE, 0); + + tree_column_props[PROP_VISIBLE] = + g_param_spec_boolean ("visible", NULL, NULL, + TRUE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + + tree_column_props[PROP_RESIZABLE] = + g_param_spec_boolean ("resizable", NULL, NULL, + FALSE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + + tree_column_props[PROP_X_OFFSET] = + g_param_spec_int ("x-offset", NULL, NULL, + -G_MAXINT, G_MAXINT, + 0, + GTK_PARAM_READABLE); + + tree_column_props[PROP_WIDTH] = + g_param_spec_int ("width", NULL, NULL, + 0, G_MAXINT, + 0, + GTK_PARAM_READABLE); + + tree_column_props[PROP_SPACING] = + g_param_spec_int ("spacing", NULL, NULL, + 0, G_MAXINT, + 0, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + + tree_column_props[PROP_SIZING] = + g_param_spec_enum ("sizing", NULL, NULL, + GTK_TYPE_TREE_VIEW_COLUMN_SIZING, + GTK_TREE_VIEW_COLUMN_GROW_ONLY, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + + tree_column_props[PROP_FIXED_WIDTH] = + g_param_spec_int ("fixed-width", NULL, NULL, + -1, G_MAXINT, + -1, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + + tree_column_props[PROP_MIN_WIDTH] = + g_param_spec_int ("min-width", NULL, NULL, + -1, G_MAXINT, + -1, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + + tree_column_props[PROP_MAX_WIDTH] = + g_param_spec_int ("max-width", NULL, NULL, + -1, G_MAXINT, + -1, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + + tree_column_props[PROP_TITLE] = + g_param_spec_string ("title", NULL, NULL, + "", + GTK_PARAM_READWRITE); + + tree_column_props[PROP_EXPAND] = + g_param_spec_boolean ("expand", NULL, NULL, + FALSE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + + tree_column_props[PROP_CLICKABLE] = + g_param_spec_boolean ("clickable", NULL, NULL, + FALSE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + + tree_column_props[PROP_WIDGET] = + g_param_spec_object ("widget", NULL, NULL, + GTK_TYPE_WIDGET, + GTK_PARAM_READWRITE); + + tree_column_props[PROP_ALIGNMENT] = + g_param_spec_float ("alignment", NULL, NULL, + 0.0, 1.0, 0.0, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + + tree_column_props[PROP_REORDERABLE] = + g_param_spec_boolean ("reorderable", NULL, NULL, + FALSE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + + tree_column_props[PROP_SORT_INDICATOR] = + g_param_spec_boolean ("sort-indicator", NULL, NULL, + FALSE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + + tree_column_props[PROP_SORT_ORDER] = + g_param_spec_enum ("sort-order", NULL, NULL, + GTK_TYPE_SORT_TYPE, + GTK_SORT_ASCENDING, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + + /** + * GtkTreeViewColumn:sort-column-id: + * + * Logical sort column ID this column sorts on when selected for sorting. Setting the sort column ID makes the column header + * clickable. Set to -1 to make the column unsortable. + **/ + tree_column_props[PROP_SORT_COLUMN_ID] = + g_param_spec_int ("sort-column-id", NULL, NULL, + -1, G_MAXINT, + -1, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + + /** + * GtkTreeViewColumn:cell-area: + * + * The `GtkCellArea` used to layout cell renderers for this column. + * + * If no area is specified when creating the tree view column with gtk_tree_view_column_new_with_area() + * a horizontally oriented `GtkCellAreaBox` will be used. + */ + tree_column_props[PROP_CELL_AREA] = + g_param_spec_object ("cell-area", NULL, NULL, + GTK_TYPE_CELL_AREA, + GTK_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY); + + g_object_class_install_properties (object_class, LAST_PROP, tree_column_props); +} + +static void +gtk_tree_view_column_custom_tag_end (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const char *tagname, + gpointer data) +{ + /* Just ignore the boolean return from here */ + _gtk_cell_layout_buildable_custom_tag_end (buildable, builder, child, tagname, data); +} + +static void +gtk_tree_view_column_buildable_init (GtkBuildableIface *iface) +{ + iface->add_child = _gtk_cell_layout_buildable_add_child; + iface->custom_tag_start = _gtk_cell_layout_buildable_custom_tag_start; + iface->custom_tag_end = gtk_tree_view_column_custom_tag_end; +} + +static void +gtk_tree_view_column_cell_layout_init (GtkCellLayoutIface *iface) +{ + iface->get_area = gtk_tree_view_column_cell_layout_get_area; +} + +static void +gtk_tree_view_column_init (GtkTreeViewColumn *tree_column) +{ + GtkTreeViewColumnPrivate *priv; + + tree_column->priv = gtk_tree_view_column_get_instance_private (tree_column); + priv = tree_column->priv; + + priv->button = NULL; + priv->xalign = 0.0; + priv->width = 0; + priv->padding = 0; + priv->min_width = -1; + priv->max_width = -1; + priv->column_type = GTK_TREE_VIEW_COLUMN_GROW_ONLY; + priv->visible = TRUE; + priv->resizable = FALSE; + priv->expand = FALSE; + priv->clickable = FALSE; + priv->dirty = TRUE; + priv->sort_order = GTK_SORT_ASCENDING; + priv->show_sort_indicator = FALSE; + priv->property_changed_signal = 0; + priv->sort_clicked_signal = 0; + priv->sort_column_changed_signal = 0; + priv->sort_column_id = -1; + priv->reorderable = FALSE; + priv->maybe_reordered = FALSE; + priv->fixed_width = -1; + priv->title = g_strdup (""); + + gtk_tree_view_column_create_button (tree_column); +} + +static void +gtk_tree_view_column_constructed (GObject *object) +{ + GtkTreeViewColumn *tree_column = GTK_TREE_VIEW_COLUMN (object); + + G_OBJECT_CLASS (gtk_tree_view_column_parent_class)->constructed (object); + + gtk_tree_view_column_ensure_cell_area (tree_column, NULL); +} + +static void +gtk_tree_view_column_dispose (GObject *object) +{ + GtkTreeViewColumn *tree_column = (GtkTreeViewColumn *) object; + GtkTreeViewColumnPrivate *priv = tree_column->priv; + + /* Remove this column from its treeview, + * in case this column is destroyed before its treeview. + */ + if (priv->tree_view) + gtk_tree_view_remove_column (GTK_TREE_VIEW (priv->tree_view), tree_column); + + if (priv->cell_area_context) + { + g_signal_handler_disconnect (priv->cell_area_context, + priv->context_changed_signal); + + g_object_unref (priv->cell_area_context); + + priv->cell_area_context = NULL; + priv->context_changed_signal = 0; + } + + if (priv->cell_area) + { + g_signal_handler_disconnect (priv->cell_area, + priv->add_editable_signal); + g_signal_handler_disconnect (priv->cell_area, + priv->remove_editable_signal); + + g_object_unref (priv->cell_area); + priv->cell_area = NULL; + priv->add_editable_signal = 0; + priv->remove_editable_signal = 0; + } + + if (priv->child) + { + g_object_unref (priv->child); + priv->child = NULL; + } + + g_clear_object (&priv->button); + + G_OBJECT_CLASS (gtk_tree_view_column_parent_class)->dispose (object); +} + +static void +gtk_tree_view_column_finalize (GObject *object) +{ + GtkTreeViewColumn *tree_column = (GtkTreeViewColumn *) object; + GtkTreeViewColumnPrivate *priv = tree_column->priv; + + g_free (priv->title); + + G_OBJECT_CLASS (gtk_tree_view_column_parent_class)->finalize (object); +} + +static void +gtk_tree_view_column_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkTreeViewColumn *tree_column; + GtkCellArea *area; + + tree_column = GTK_TREE_VIEW_COLUMN (object); + + switch (prop_id) + { + case PROP_VISIBLE: + gtk_tree_view_column_set_visible (tree_column, + g_value_get_boolean (value)); + break; + + case PROP_RESIZABLE: + gtk_tree_view_column_set_resizable (tree_column, + g_value_get_boolean (value)); + break; + + case PROP_SIZING: + gtk_tree_view_column_set_sizing (tree_column, + g_value_get_enum (value)); + break; + + case PROP_FIXED_WIDTH: + gtk_tree_view_column_set_fixed_width (tree_column, + g_value_get_int (value)); + break; + + case PROP_MIN_WIDTH: + gtk_tree_view_column_set_min_width (tree_column, + g_value_get_int (value)); + break; + + case PROP_MAX_WIDTH: + gtk_tree_view_column_set_max_width (tree_column, + g_value_get_int (value)); + break; + + case PROP_SPACING: + gtk_tree_view_column_set_spacing (tree_column, + g_value_get_int (value)); + break; + + case PROP_TITLE: + gtk_tree_view_column_set_title (tree_column, + g_value_get_string (value)); + break; + + case PROP_EXPAND: + gtk_tree_view_column_set_expand (tree_column, + g_value_get_boolean (value)); + break; + + case PROP_CLICKABLE: + gtk_tree_view_column_set_clickable (tree_column, + g_value_get_boolean (value)); + break; + + case PROP_WIDGET: + gtk_tree_view_column_set_widget (tree_column, + (GtkWidget*) g_value_get_object (value)); + break; + + case PROP_ALIGNMENT: + gtk_tree_view_column_set_alignment (tree_column, + g_value_get_float (value)); + break; + + case PROP_REORDERABLE: + gtk_tree_view_column_set_reorderable (tree_column, + g_value_get_boolean (value)); + break; + + case PROP_SORT_INDICATOR: + gtk_tree_view_column_set_sort_indicator (tree_column, + g_value_get_boolean (value)); + break; + + case PROP_SORT_ORDER: + gtk_tree_view_column_set_sort_order (tree_column, + g_value_get_enum (value)); + break; + + case PROP_SORT_COLUMN_ID: + gtk_tree_view_column_set_sort_column_id (tree_column, + g_value_get_int (value)); + break; + + case PROP_CELL_AREA: + /* Construct-only, can only be assigned once */ + area = g_value_get_object (value); + + if (area) + { + if (tree_column->priv->cell_area != NULL) + { + g_warning ("cell-area has already been set, ignoring construct property"); + g_object_ref_sink (area); + g_object_unref (area); + } + else + gtk_tree_view_column_ensure_cell_area (tree_column, area); + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_tree_view_column_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkTreeViewColumn *tree_column; + + tree_column = GTK_TREE_VIEW_COLUMN (object); + + switch (prop_id) + { + case PROP_VISIBLE: + g_value_set_boolean (value, + gtk_tree_view_column_get_visible (tree_column)); + break; + + case PROP_RESIZABLE: + g_value_set_boolean (value, + gtk_tree_view_column_get_resizable (tree_column)); + break; + + case PROP_X_OFFSET: + g_value_set_int (value, + gtk_tree_view_column_get_x_offset (tree_column)); + break; + + case PROP_WIDTH: + g_value_set_int (value, + gtk_tree_view_column_get_width (tree_column)); + break; + + case PROP_SPACING: + g_value_set_int (value, + gtk_tree_view_column_get_spacing (tree_column)); + break; + + case PROP_SIZING: + g_value_set_enum (value, + gtk_tree_view_column_get_sizing (tree_column)); + break; + + case PROP_FIXED_WIDTH: + g_value_set_int (value, + gtk_tree_view_column_get_fixed_width (tree_column)); + break; + + case PROP_MIN_WIDTH: + g_value_set_int (value, + gtk_tree_view_column_get_min_width (tree_column)); + break; + + case PROP_MAX_WIDTH: + g_value_set_int (value, + gtk_tree_view_column_get_max_width (tree_column)); + break; + + case PROP_TITLE: + g_value_set_string (value, + gtk_tree_view_column_get_title (tree_column)); + break; + + case PROP_EXPAND: + g_value_set_boolean (value, + gtk_tree_view_column_get_expand (tree_column)); + break; + + case PROP_CLICKABLE: + g_value_set_boolean (value, + gtk_tree_view_column_get_clickable (tree_column)); + break; + + case PROP_WIDGET: + g_value_set_object (value, + (GObject*) gtk_tree_view_column_get_widget (tree_column)); + break; + + case PROP_ALIGNMENT: + g_value_set_float (value, + gtk_tree_view_column_get_alignment (tree_column)); + break; + + case PROP_REORDERABLE: + g_value_set_boolean (value, + gtk_tree_view_column_get_reorderable (tree_column)); + break; + + case PROP_SORT_INDICATOR: + g_value_set_boolean (value, + gtk_tree_view_column_get_sort_indicator (tree_column)); + break; + + case PROP_SORT_ORDER: + g_value_set_enum (value, + gtk_tree_view_column_get_sort_order (tree_column)); + break; + + case PROP_SORT_COLUMN_ID: + g_value_set_int (value, + gtk_tree_view_column_get_sort_column_id (tree_column)); + break; + + case PROP_CELL_AREA: + g_value_set_object (value, tree_column->priv->cell_area); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +/* Implementation of GtkCellLayout interface + */ + +static void +gtk_tree_view_column_ensure_cell_area (GtkTreeViewColumn *column, + GtkCellArea *cell_area) +{ + GtkTreeViewColumnPrivate *priv = column->priv; + + if (priv->cell_area) + return; + + if (cell_area) + priv->cell_area = cell_area; + else + priv->cell_area = gtk_cell_area_box_new (); + + g_object_ref_sink (priv->cell_area); + + priv->add_editable_signal = + g_signal_connect (priv->cell_area, "add-editable", + G_CALLBACK (gtk_tree_view_column_add_editable_callback), + column); + priv->remove_editable_signal = + g_signal_connect (priv->cell_area, "remove-editable", + G_CALLBACK (gtk_tree_view_column_remove_editable_callback), + column); + + priv->cell_area_context = gtk_cell_area_create_context (priv->cell_area); + + priv->context_changed_signal = + g_signal_connect (priv->cell_area_context, "notify", + G_CALLBACK (gtk_tree_view_column_context_changed), + column); +} + +static GtkCellArea * +gtk_tree_view_column_cell_layout_get_area (GtkCellLayout *cell_layout) +{ + GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN (cell_layout); + GtkTreeViewColumnPrivate *priv = column->priv; + + if (G_UNLIKELY (!priv->cell_area)) + gtk_tree_view_column_ensure_cell_area (column, NULL); + + return priv->cell_area; +} + +static void +focus_in (GtkEventControllerKey *controller, + GtkTreeViewColumn *column) +{ + _gtk_tree_view_set_focus_column (GTK_TREE_VIEW (column->priv->tree_view), column); +} + +/* Button handling code + */ +static void +gtk_tree_view_column_create_button (GtkTreeViewColumn *tree_column) +{ + GtkTreeViewColumnPrivate *priv = tree_column->priv; + GtkEventController *controller; + GtkWidget *child; + GtkWidget *hbox; + + g_return_if_fail (priv->button == NULL); + + priv->button = gtk_button_new (); + g_object_ref_sink (priv->button); + gtk_widget_set_focus_on_click (priv->button, FALSE); + gtk_widget_set_overflow (priv->button, GTK_OVERFLOW_HIDDEN); + + g_signal_connect (priv->button, "clicked", + G_CALLBACK (gtk_tree_view_column_button_clicked), + tree_column); + + controller = GTK_EVENT_CONTROLLER (gtk_gesture_drag_new ()); + g_signal_connect (controller, "drag-begin", + G_CALLBACK (column_button_drag_begin), tree_column); + g_signal_connect (controller, "drag-update", + G_CALLBACK (column_button_drag_update), tree_column); + gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_CAPTURE); + gtk_widget_add_controller (priv->button, controller); + + controller = gtk_event_controller_focus_new (); + g_signal_connect (controller, "enter", G_CALLBACK (focus_in), tree_column); + gtk_widget_add_controller (priv->button, controller); + + priv->frame = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + gtk_widget_set_hexpand (priv->frame, TRUE); + gtk_widget_set_halign (priv->frame, GTK_ALIGN_START); + + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2); + priv->arrow = gtk_builtin_icon_new ("sort-indicator"); + + if (priv->child) + child = priv->child; + else + child = gtk_label_new (priv->title); + + g_signal_connect (child, "mnemonic-activate", + G_CALLBACK (gtk_tree_view_column_mnemonic_activate), + tree_column); + + if (priv->xalign <= 0.5) + { + gtk_box_append (GTK_BOX (hbox), priv->frame); + gtk_box_append (GTK_BOX (hbox), priv->arrow); + } + else + { + gtk_box_append (GTK_BOX (hbox), priv->arrow); + gtk_box_append (GTK_BOX (hbox), priv->frame); + } + + gtk_box_append (GTK_BOX (priv->frame), child); + gtk_button_set_child (GTK_BUTTON (priv->button), hbox); +} + +static void +gtk_tree_view_column_update_button (GtkTreeViewColumn *tree_column) +{ + GtkTreeViewColumnPrivate *priv = tree_column->priv; + int sort_column_id = -1; + GtkWidget *hbox; + GtkWidget *frame; + GtkWidget *arrow; + GtkWidget *current_child; + GtkTreeModel *model; + + if (priv->tree_view) + model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree_view)); + else + model = NULL; + + hbox = gtk_button_get_child (GTK_BUTTON (priv->button)); + frame = priv->frame; + arrow = priv->arrow; + current_child = gtk_widget_get_first_child (frame); + + /* Set up the actual button */ + if (priv->child) + { + if (current_child != priv->child) + { + gtk_box_remove (GTK_BOX (frame), current_child); + gtk_box_append (GTK_BOX (frame), priv->child); + } + } + else + { + if (current_child == NULL) + { + current_child = gtk_label_new (NULL); + gtk_widget_show (current_child); + gtk_box_append (GTK_BOX (frame), current_child); + } + + g_return_if_fail (GTK_IS_LABEL (current_child)); + + if (priv->title) + gtk_label_set_text_with_mnemonic (GTK_LABEL (current_child), + priv->title); + else + gtk_label_set_text_with_mnemonic (GTK_LABEL (current_child), + ""); + } + + if (GTK_IS_TREE_SORTABLE (model)) + gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (model), + &sort_column_id, + NULL); + + if (priv->show_sort_indicator) + { + gboolean alternative; + + if (priv->tree_view) + g_object_get (gtk_widget_get_settings (priv->tree_view), + "gtk-alternative-sort-arrows", &alternative, + NULL); + else + alternative = FALSE; + + if ((!alternative && priv->sort_order == GTK_SORT_ASCENDING) || + (alternative && priv->sort_order == GTK_SORT_DESCENDING)) + { + gtk_widget_remove_css_class (arrow, "descending"); + gtk_widget_add_css_class (arrow, "ascending"); + } + else + { + gtk_widget_remove_css_class (arrow, "ascending"); + gtk_widget_add_css_class (arrow, "descending"); + } + } + + /* Put arrow on the right if the text is left-or-center justified, and on the + * left otherwise; do this by packing boxes, so flipping text direction will + * reverse things + */ + if (priv->xalign <= 0.5) + gtk_box_reorder_child_after (GTK_BOX (hbox), arrow, gtk_widget_get_last_child (hbox)); + else + gtk_box_reorder_child_after (GTK_BOX (hbox), arrow, NULL); + + if (priv->show_sort_indicator + || (GTK_IS_TREE_SORTABLE (model) && priv->sort_column_id >= 0)) + gtk_widget_show (arrow); + else + gtk_widget_hide (arrow); + + if (priv->show_sort_indicator) + gtk_widget_set_opacity (arrow, 1.0); + else + gtk_widget_set_opacity (arrow, 0.0); + + /* It's always safe to hide the button. It isn't always safe to show it, as + * if you show it before it's realized, it'll get the wrong window. */ + if (priv->tree_view != NULL && + gtk_widget_get_realized (priv->tree_view)) + { + if (priv->visible && + gtk_tree_view_get_headers_visible (GTK_TREE_VIEW (priv->tree_view))) + { + gtk_widget_show (priv->button); + } + else + { + gtk_widget_hide (priv->button); + } + } + + if (priv->reorderable || priv->clickable) + { + gtk_widget_set_focusable (priv->button, TRUE); + } + else + { + gtk_widget_set_focusable (priv->button, FALSE); + if (gtk_widget_has_focus (priv->button)) + { + GtkRoot *root = gtk_widget_get_root (priv->tree_view); + gtk_root_set_focus (root, NULL); + } + } + /* Queue a resize on the assumption that we always want to catch all changes + * and columns don't change all that often. + */ + if (priv->tree_view && gtk_widget_get_realized (priv->tree_view)) + gtk_widget_queue_resize (priv->tree_view); +} + +/* Button signal handlers + */ + +static void +column_button_drag_begin (GtkGestureDrag *gesture, + double x, + double y, + GtkTreeViewColumn *column) +{ + GtkTreeViewColumnPrivate *priv = column->priv; + + if (!priv->reorderable) + { + gtk_gesture_set_state (GTK_GESTURE (gesture), + GTK_EVENT_SEQUENCE_DENIED); + return; + } + + priv->drag_x = x; + priv->drag_y = y; + gtk_widget_grab_focus (priv->button); +} + +static void +column_button_drag_update (GtkGestureDrag *gesture, + double offset_x, + double offset_y, + GtkTreeViewColumn *column) +{ + GtkTreeViewColumnPrivate *priv = column->priv; + + if (gtk_drag_check_threshold_double (priv->button, 0, 0, offset_x, offset_y)) + { + gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED); + _gtk_tree_view_column_start_drag (GTK_TREE_VIEW (priv->tree_view), column, + gtk_gesture_get_device (GTK_GESTURE (gesture))); + } +} + +static void +gtk_tree_view_column_button_clicked (GtkWidget *widget, gpointer data) +{ + g_signal_emit_by_name (data, "clicked"); +} + +static gboolean +gtk_tree_view_column_mnemonic_activate (GtkWidget *widget, + gboolean group_cycling, + gpointer data) +{ + GtkTreeViewColumn *column = (GtkTreeViewColumn *)data; + GtkTreeViewColumnPrivate *priv = column->priv; + + g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (column), FALSE); + + _gtk_tree_view_set_focus_column (GTK_TREE_VIEW (priv->tree_view), column); + + if (priv->clickable) + g_signal_emit_by_name (priv->button, "clicked"); + else if (gtk_widget_get_focusable (priv->button)) + gtk_widget_grab_focus (priv->button); + else + gtk_widget_grab_focus (priv->tree_view); + + return TRUE; +} + +static void +gtk_tree_view_model_sort_column_changed (GtkTreeSortable *sortable, + GtkTreeViewColumn *column) +{ + GtkTreeViewColumnPrivate *priv = column->priv; + int sort_column_id; + GtkSortType order; + + if (gtk_tree_sortable_get_sort_column_id (sortable, + &sort_column_id, + &order)) + { + if (sort_column_id == priv->sort_column_id) + { + gtk_tree_view_column_set_sort_indicator (column, TRUE); + gtk_tree_view_column_set_sort_order (column, order); + } + else + { + gtk_tree_view_column_set_sort_indicator (column, FALSE); + } + } + else + { + gtk_tree_view_column_set_sort_indicator (column, FALSE); + } +} + +static void +gtk_tree_view_column_sort (GtkTreeViewColumn *tree_column, + gpointer data) +{ + GtkTreeViewColumnPrivate *priv = tree_column->priv; + GtkTreeModel *model; + GtkTreeSortable *sortable; + int sort_column_id; + GtkSortType order; + gboolean has_sort_column; + gboolean has_default_sort_func; + + g_return_if_fail (priv->tree_view != NULL); + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree_view)); + sortable = GTK_TREE_SORTABLE (model); + + has_sort_column = + gtk_tree_sortable_get_sort_column_id (sortable, + &sort_column_id, + &order); + has_default_sort_func = + gtk_tree_sortable_has_default_sort_func (sortable); + + if (has_sort_column && + sort_column_id == priv->sort_column_id) + { + if (order == GTK_SORT_ASCENDING) + gtk_tree_sortable_set_sort_column_id (sortable, + priv->sort_column_id, + GTK_SORT_DESCENDING); + else if (order == GTK_SORT_DESCENDING && has_default_sort_func) + gtk_tree_sortable_set_sort_column_id (sortable, + GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, + GTK_SORT_ASCENDING); + else + gtk_tree_sortable_set_sort_column_id (sortable, + priv->sort_column_id, + GTK_SORT_ASCENDING); + } + else + { + gtk_tree_sortable_set_sort_column_id (sortable, + priv->sort_column_id, + GTK_SORT_ASCENDING); + } +} + +static void +gtk_tree_view_column_setup_sort_column_id_callback (GtkTreeViewColumn *tree_column) +{ + GtkTreeViewColumnPrivate *priv = tree_column->priv; + GtkTreeModel *model; + + if (priv->tree_view == NULL) + return; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree_view)); + + if (model == NULL) + return; + + if (GTK_IS_TREE_SORTABLE (model) && + priv->sort_column_id != -1) + { + int real_sort_column_id; + GtkSortType real_order; + + if (priv->sort_column_changed_signal == 0) + priv->sort_column_changed_signal = + g_signal_connect (model, "sort-column-changed", + G_CALLBACK (gtk_tree_view_model_sort_column_changed), + tree_column); + + if (gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (model), + &real_sort_column_id, + &real_order) && + (real_sort_column_id == priv->sort_column_id)) + { + gtk_tree_view_column_set_sort_indicator (tree_column, TRUE); + gtk_tree_view_column_set_sort_order (tree_column, real_order); + } + else + { + gtk_tree_view_column_set_sort_indicator (tree_column, FALSE); + } + } +} + +static void +gtk_tree_view_column_context_changed (GtkCellAreaContext *context, + GParamSpec *pspec, + GtkTreeViewColumn *tree_column) +{ + /* Here we want the column re-requested if the underlying context was + * actually reset for any reason, this can happen if the underlying + * area/cell configuration changes (i.e. cell packing properties + * or cell spacing and the like) + * + * Note that we block this handler while requesting for sizes + * so there is no need to check for the new context size being -1, + * we also block the handler when explicitly resetting the context + * so as to avoid some infinite stack recursion. + */ + if (!strcmp (pspec->name, "minimum-width") || + !strcmp (pspec->name, "natural-width") || + !strcmp (pspec->name, "minimum-height") || + !strcmp (pspec->name, "natural-height")) + _gtk_tree_view_column_cell_set_dirty (tree_column, TRUE); +} + +static void +gtk_tree_view_column_add_editable_callback (GtkCellArea *area, + GtkCellRenderer *renderer, + GtkCellEditable *edit_widget, + GdkRectangle *cell_area, + const char *path_string, + gpointer user_data) +{ + GtkTreeViewColumn *column = user_data; + GtkTreeViewColumnPrivate *priv = column->priv; + GtkTreePath *path; + + if (priv->tree_view) + { + path = gtk_tree_path_new_from_string (path_string); + + _gtk_tree_view_add_editable (GTK_TREE_VIEW (priv->tree_view), + column, + path, + edit_widget, + cell_area); + + gtk_tree_path_free (path); + } +} + +static void +gtk_tree_view_column_remove_editable_callback (GtkCellArea *area, + GtkCellRenderer *renderer, + GtkCellEditable *edit_widget, + gpointer user_data) +{ + GtkTreeViewColumn *column = user_data; + GtkTreeViewColumnPrivate *priv = column->priv; + + if (priv->tree_view) + _gtk_tree_view_remove_editable (GTK_TREE_VIEW (priv->tree_view), + column, + edit_widget); +} + +/* Exported Private Functions. + * These should only be called by gtktreeview.c or gtktreeviewcolumn.c + */ +void +_gtk_tree_view_column_realize_button (GtkTreeViewColumn *column) +{ + g_return_if_fail (GTK_IS_TREE_VIEW (column->priv->tree_view)); + g_return_if_fail (gtk_widget_get_realized (column->priv->tree_view)); + g_return_if_fail (column->priv->button != NULL); + + gtk_tree_view_column_update_button (column); +} + +void +_gtk_tree_view_column_unset_model (GtkTreeViewColumn *column, + GtkTreeModel *old_model) +{ + GtkTreeViewColumnPrivate *priv = column->priv; + + if (priv->sort_column_changed_signal) + { + g_signal_handler_disconnect (old_model, + priv->sort_column_changed_signal); + priv->sort_column_changed_signal = 0; + } + gtk_tree_view_column_set_sort_indicator (column, FALSE); +} + +void +_gtk_tree_view_column_set_tree_view (GtkTreeViewColumn *column, + GtkTreeView *tree_view) +{ + GtkTreeViewColumnPrivate *priv = column->priv; + + g_assert (priv->tree_view == NULL); + + priv->tree_view = GTK_WIDGET (tree_view); + + /* avoid a warning with our messed up CSS nodes */ + gtk_widget_insert_after (priv->button, GTK_WIDGET (tree_view), NULL); + + priv->property_changed_signal = + g_signal_connect_swapped (tree_view, + "notify::model", + G_CALLBACK (gtk_tree_view_column_setup_sort_column_id_callback), + column); + + gtk_tree_view_column_setup_sort_column_id_callback (column); +} + +void +_gtk_tree_view_column_unset_tree_view (GtkTreeViewColumn *column) +{ + GtkTreeViewColumnPrivate *priv = column->priv; + + if (priv->tree_view == NULL) + return; + + gtk_widget_unparent (priv->button); + + if (priv->property_changed_signal) + { + g_signal_handler_disconnect (priv->tree_view, priv->property_changed_signal); + priv->property_changed_signal = 0; + } + + if (priv->sort_column_changed_signal) + { + g_signal_handler_disconnect (gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree_view)), + priv->sort_column_changed_signal); + priv->sort_column_changed_signal = 0; + } + + priv->tree_view = NULL; +} + +gboolean +_gtk_tree_view_column_has_editable_cell (GtkTreeViewColumn *column) +{ + GtkTreeViewColumnPrivate *priv = column->priv; + gboolean ret = FALSE; + GList *list, *cells; + + cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (priv->cell_area)); + + for (list = cells; list; list = list->next) + { + GtkCellRenderer *cell = list->data; + GtkCellRendererMode mode; + + g_object_get (cell, "mode", &mode, NULL); + + if (mode == GTK_CELL_RENDERER_MODE_EDITABLE) + { + ret = TRUE; + break; + } + } + + g_list_free (cells); + + return ret; +} + +/* gets cell being edited */ +GtkCellRenderer * +_gtk_tree_view_column_get_edited_cell (GtkTreeViewColumn *column) +{ + GtkTreeViewColumnPrivate *priv = column->priv; + + return gtk_cell_area_get_edited_cell (priv->cell_area); +} + +GtkCellRenderer * +_gtk_tree_view_column_get_cell_at_pos (GtkTreeViewColumn *column, + GdkRectangle *cell_area, + GdkRectangle *background_area, + int x, + int y) +{ + GtkCellRenderer *match = NULL; + GtkTreeViewColumnPrivate *priv = column->priv; + + /* If (x, y) is outside of the background area, immediately return */ + if (x < background_area->x || + x > background_area->x + background_area->width || + y < background_area->y || + y > background_area->y + background_area->height) + return NULL; + + /* If (x, y) is inside the background area, clamp it to the cell_area + * so that a cell is still returned. The main reason for doing this + * (on the x axis) is for handling clicks in the indentation area + * (either at the left or right depending on RTL setting). Another + * reason is for handling clicks on the area where the focus rectangle + * is drawn (this is outside of cell area), this manifests itself + * mainly when a large setting is used for focus-line-width. + */ + if (x < cell_area->x) + x = cell_area->x; + else if (x > cell_area->x + cell_area->width) + x = cell_area->x + cell_area->width; + + if (y < cell_area->y) + y = cell_area->y; + else if (y > cell_area->y + cell_area->height) + y = cell_area->y + cell_area->height; + + match = gtk_cell_area_get_cell_at_position (priv->cell_area, + priv->cell_area_context, + priv->tree_view, + cell_area, + x, y, + NULL); + + return match; +} + +gboolean +_gtk_tree_view_column_is_blank_at_pos (GtkTreeViewColumn *column, + GdkRectangle *cell_area, + GdkRectangle *background_area, + int x, + int y) +{ + GtkCellRenderer *match; + GdkRectangle cell_alloc, aligned_area, inner_area; + GtkTreeViewColumnPrivate *priv = column->priv; + + match = _gtk_tree_view_column_get_cell_at_pos (column, + cell_area, + background_area, + x, y); + if (!match) + return FALSE; + + gtk_cell_area_get_cell_allocation (priv->cell_area, + priv->cell_area_context, + priv->tree_view, + match, + cell_area, + &cell_alloc); + + gtk_cell_area_inner_cell_area (priv->cell_area, priv->tree_view, + &cell_alloc, &inner_area); + gtk_cell_renderer_get_aligned_area (match, priv->tree_view, 0, + &inner_area, &aligned_area); + + if (x < aligned_area.x || + x > aligned_area.x + aligned_area.width || + y < aligned_area.y || + y > aligned_area.y + aligned_area.height) + return TRUE; + + return FALSE; +} + +/* Public Functions */ + + +/** + * gtk_tree_view_column_new: + * + * Creates a new `GtkTreeViewColumn`. + * + * Returns: A newly created `GtkTreeViewColumn`. + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +GtkTreeViewColumn * +gtk_tree_view_column_new (void) +{ + GtkTreeViewColumn *tree_column; + + tree_column = g_object_new (GTK_TYPE_TREE_VIEW_COLUMN, NULL); + + return tree_column; +} + +/** + * gtk_tree_view_column_new_with_area: + * @area: the `GtkCellArea` that the newly created column should use to layout cells. + * + * Creates a new `GtkTreeViewColumn` using @area to render its cells. + * + * Returns: A newly created `GtkTreeViewColumn`. + * + * Deprecated: 4.10: Use GtkColumnView instead + */ +GtkTreeViewColumn * +gtk_tree_view_column_new_with_area (GtkCellArea *area) +{ + GtkTreeViewColumn *tree_column; + + tree_column = g_object_new (GTK_TYPE_TREE_VIEW_COLUMN, "cell-area", area, NULL); + + return tree_column; +} + + +/** + * gtk_tree_view_column_new_with_attributes: + * @title: The title to set the header to + * @cell: The `GtkCellRenderer` + * @...: A %NULL-terminated list of attributes + * + * Creates a new `GtkTreeViewColumn` with a number of default values. + * This is equivalent to calling gtk_tree_view_column_set_title(), + * gtk_tree_view_column_pack_start(), and + * gtk_tree_view_column_set_attributes() on the newly created `GtkTreeViewColumn`. + * + * Here’s a simple example: + * |[ + * enum { TEXT_COLUMN, COLOR_COLUMN, N_COLUMNS }; + * // ... + * { + * GtkTreeViewColumn *column; + * GtkCellRenderer *renderer = gtk_cell_renderer_text_new (); + * + * column = gtk_tree_view_column_new_with_attributes ("Title", + * renderer, + * "text", TEXT_COLUMN, + * "foreground", COLOR_COLUMN, + * NULL); + * } + * ]| + * + * Returns: A newly created `GtkTreeViewColumn`. + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +GtkTreeViewColumn * +gtk_tree_view_column_new_with_attributes (const char *title, + GtkCellRenderer *cell, + ...) +{ + GtkTreeViewColumn *retval; + va_list args; + + retval = gtk_tree_view_column_new (); + + gtk_tree_view_column_set_title (retval, title); + gtk_tree_view_column_pack_start (retval, cell, TRUE); + + va_start (args, cell); + gtk_tree_view_column_set_attributesv (retval, cell, args); + va_end (args); + + return retval; +} + +/** + * gtk_tree_view_column_pack_start: + * @tree_column: A `GtkTreeViewColumn`. + * @cell: The `GtkCellRenderer` + * @expand: %TRUE if @cell is to be given extra space allocated to @tree_column. + * + * Packs the @cell into the beginning of the column. If @expand is %FALSE, then + * the @cell is allocated no more space than it needs. Any unused space is divided + * evenly between cells for which @expand is %TRUE. + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +void +gtk_tree_view_column_pack_start (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + gboolean expand) +{ + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (tree_column), cell, expand); +} + +/** + * gtk_tree_view_column_pack_end: + * @tree_column: A `GtkTreeViewColumn`. + * @cell: The `GtkCellRenderer` + * @expand: %TRUE if @cell is to be given extra space allocated to @tree_column. + * + * Adds the @cell to end of the column. If @expand is %FALSE, then the @cell + * is allocated no more space than it needs. Any unused space is divided + * evenly between cells for which @expand is %TRUE. + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +void +gtk_tree_view_column_pack_end (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + gboolean expand) +{ + gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (tree_column), cell, expand); +} + +/** + * gtk_tree_view_column_clear: + * @tree_column: A `GtkTreeViewColumn` + * + * Unsets all the mappings on all renderers on the @tree_column. + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +void +gtk_tree_view_column_clear (GtkTreeViewColumn *tree_column) +{ + gtk_cell_layout_clear (GTK_CELL_LAYOUT (tree_column)); +} + +/** + * gtk_tree_view_column_add_attribute: + * @tree_column: A `GtkTreeViewColumn` + * @cell_renderer: the `GtkCellRenderer` to set attributes on + * @attribute: An attribute on the renderer + * @column: The column position on the model to get the attribute from. + * + * Adds an attribute mapping to the list in @tree_column. + * + * The @column is the + * column of the model to get a value from, and the @attribute is the + * parameter on @cell_renderer to be set from the value. So for example + * if column 2 of the model contains strings, you could have the + * “text” attribute of a `GtkCellRendererText` get its values from + * column 2. + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +void +gtk_tree_view_column_add_attribute (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell_renderer, + const char *attribute, + int column) +{ + gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (tree_column), + cell_renderer, attribute, column); +} + +static void +gtk_tree_view_column_set_attributesv (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell_renderer, + va_list args) +{ + GtkTreeViewColumnPrivate *priv = tree_column->priv; + char *attribute; + int column; + + attribute = va_arg (args, char *); + + gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (priv->cell_area), + cell_renderer); + + while (attribute != NULL) + { + column = va_arg (args, int); + gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (priv->cell_area), + cell_renderer, attribute, column); + attribute = va_arg (args, char *); + } +} + +/** + * gtk_tree_view_column_set_attributes: + * @tree_column: A `GtkTreeViewColumn` + * @cell_renderer: the `GtkCellRenderer` we’re setting the attributes of + * @...: A %NULL-terminated list of attributes + * + * Sets the attributes in the list as the attributes of @tree_column. + * + * The attributes should be in attribute/column order, as in + * gtk_tree_view_column_add_attribute(). All existing attributes + * are removed, and replaced with the new attributes. + * + * Deprecated: 4.10: Use GtkColumnView instead + */ +void +gtk_tree_view_column_set_attributes (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell_renderer, + ...) +{ + va_list args; + + g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); + g_return_if_fail (GTK_IS_CELL_RENDERER (cell_renderer)); + + va_start (args, cell_renderer); + gtk_tree_view_column_set_attributesv (tree_column, cell_renderer, args); + va_end (args); +} + + +/** + * gtk_tree_view_column_set_cell_data_func: + * @tree_column: A `GtkTreeViewColumn` + * @cell_renderer: A `GtkCellRenderer` + * @func: (nullable): The `GtkTreeCellDataFunc` to use. + * @func_data: (closure): The user data for @func. + * @destroy: The destroy notification for @func_data + * + * Sets the `GtkTreeCellDataFunc` to use for the column. + * + * This + * function is used instead of the standard attributes mapping for + * setting the column value, and should set the value of @tree_column's + * cell renderer as appropriate. @func may be %NULL to remove an + * older one. + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +void +gtk_tree_view_column_set_cell_data_func (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell_renderer, + GtkTreeCellDataFunc func, + gpointer func_data, + GDestroyNotify destroy) +{ + gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (tree_column), + cell_renderer, + (GtkCellLayoutDataFunc)func, + func_data, destroy); +} + + +/** + * gtk_tree_view_column_clear_attributes: + * @tree_column: a `GtkTreeViewColumn` + * @cell_renderer: a `GtkCellRenderer` to clear the attribute mapping on. + * + * Clears all existing attributes previously set with + * gtk_tree_view_column_set_attributes(). + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +void +gtk_tree_view_column_clear_attributes (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell_renderer) +{ + gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (tree_column), + cell_renderer); +} + +/** + * gtk_tree_view_column_set_spacing: + * @tree_column: A `GtkTreeViewColumn`. + * @spacing: distance between cell renderers in pixels. + * + * Sets the spacing field of @tree_column, which is the number of pixels to + * place between cell renderers packed into it. + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +void +gtk_tree_view_column_set_spacing (GtkTreeViewColumn *tree_column, + int spacing) +{ + GtkTreeViewColumnPrivate *priv; + + g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); + g_return_if_fail (spacing >= 0); + + priv = tree_column->priv; + + if (gtk_cell_area_box_get_spacing (GTK_CELL_AREA_BOX (priv->cell_area)) != spacing) + { + gtk_cell_area_box_set_spacing (GTK_CELL_AREA_BOX (priv->cell_area), spacing); + if (priv->tree_view) + _gtk_tree_view_column_cell_set_dirty (tree_column, TRUE); + g_object_notify_by_pspec (G_OBJECT (tree_column), tree_column_props[PROP_SPACING]); + } +} + +/** + * gtk_tree_view_column_get_spacing: + * @tree_column: A `GtkTreeViewColumn`. + * + * Returns the spacing of @tree_column. + * + * Returns: the spacing of @tree_column. + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +int +gtk_tree_view_column_get_spacing (GtkTreeViewColumn *tree_column) +{ + GtkTreeViewColumnPrivate *priv; + + g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), 0); + + priv = tree_column->priv; + + return gtk_cell_area_box_get_spacing (GTK_CELL_AREA_BOX (priv->cell_area)); +} + +/* Options for manipulating the columns */ + +/** + * gtk_tree_view_column_set_visible: + * @tree_column: A `GtkTreeViewColumn`. + * @visible: %TRUE if the @tree_column is visible. + * + * Sets the visibility of @tree_column. + * + * Deprecated: 4.10: Use GtkColumnView instead + */ +void +gtk_tree_view_column_set_visible (GtkTreeViewColumn *tree_column, + gboolean visible) +{ + GtkTreeViewColumnPrivate *priv; + + g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); + + priv = tree_column->priv; + visible = !! visible; + + if (priv->visible == visible) + return; + + priv->visible = visible; + + gtk_widget_set_visible (priv->button, visible); + + if (priv->visible) + _gtk_tree_view_column_cell_set_dirty (tree_column, TRUE); + + gtk_tree_view_column_update_button (tree_column); + g_object_notify_by_pspec (G_OBJECT (tree_column), tree_column_props[PROP_VISIBLE]); +} + +/** + * gtk_tree_view_column_get_visible: + * @tree_column: A `GtkTreeViewColumn`. + * + * Returns %TRUE if @tree_column is visible. + * + * Returns: whether the column is visible or not. If it is visible, then + * the tree will show the column. + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +gboolean +gtk_tree_view_column_get_visible (GtkTreeViewColumn *tree_column) +{ + g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), FALSE); + + return tree_column->priv->visible; +} + +/** + * gtk_tree_view_column_set_resizable: + * @tree_column: A `GtkTreeViewColumn` + * @resizable: %TRUE, if the column can be resized + * + * If @resizable is %TRUE, then the user can explicitly resize the column by + * grabbing the outer edge of the column button. + * + * If resizable is %TRUE and + * sizing mode of the column is %GTK_TREE_VIEW_COLUMN_AUTOSIZE, then the sizing + * mode is changed to %GTK_TREE_VIEW_COLUMN_GROW_ONLY. + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +void +gtk_tree_view_column_set_resizable (GtkTreeViewColumn *tree_column, + gboolean resizable) +{ + GtkTreeViewColumnPrivate *priv; + + g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); + + priv = tree_column->priv; + resizable = !! resizable; + + if (priv->resizable == resizable) + return; + + priv->resizable = resizable; + + if (resizable && priv->column_type == GTK_TREE_VIEW_COLUMN_AUTOSIZE) + gtk_tree_view_column_set_sizing (tree_column, GTK_TREE_VIEW_COLUMN_GROW_ONLY); + + gtk_tree_view_column_update_button (tree_column); + + g_object_notify_by_pspec (G_OBJECT (tree_column), tree_column_props[PROP_RESIZABLE]); +} + +/** + * gtk_tree_view_column_get_resizable: + * @tree_column: A `GtkTreeViewColumn` + * + * Returns %TRUE if the @tree_column can be resized by the end user. + * + * Returns: %TRUE, if the @tree_column can be resized. + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +gboolean +gtk_tree_view_column_get_resizable (GtkTreeViewColumn *tree_column) +{ + g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), FALSE); + + return tree_column->priv->resizable; +} + + +/** + * gtk_tree_view_column_set_sizing: + * @tree_column: A `GtkTreeViewColumn`. + * @type: The `GtkTreeViewColumn`Sizing. + * + * Sets the growth behavior of @tree_column to @type. + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +void +gtk_tree_view_column_set_sizing (GtkTreeViewColumn *tree_column, + GtkTreeViewColumnSizing type) +{ + GtkTreeViewColumnPrivate *priv; + + g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); + + priv = tree_column->priv; + + if (type == priv->column_type) + return; + + if (type == GTK_TREE_VIEW_COLUMN_AUTOSIZE) + gtk_tree_view_column_set_resizable (tree_column, FALSE); + + priv->column_type = type; + + gtk_tree_view_column_update_button (tree_column); + + g_object_notify_by_pspec (G_OBJECT (tree_column), tree_column_props[PROP_SIZING]); +} + +/** + * gtk_tree_view_column_get_sizing: + * @tree_column: A `GtkTreeViewColumn`. + * + * Returns the current type of @tree_column. + * + * Returns: The type of @tree_column. + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +GtkTreeViewColumnSizing +gtk_tree_view_column_get_sizing (GtkTreeViewColumn *tree_column) +{ + g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), 0); + + return tree_column->priv->column_type; +} + +/** + * gtk_tree_view_column_get_width: + * @tree_column: A `GtkTreeViewColumn`. + * + * Returns the current size of @tree_column in pixels. + * + * Returns: The current width of @tree_column. + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +int +gtk_tree_view_column_get_width (GtkTreeViewColumn *tree_column) +{ + g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), 0); + + return tree_column->priv->width; +} + +/** + * gtk_tree_view_column_get_x_offset: + * @tree_column: A `GtkTreeViewColumn`. + * + * Returns the current X offset of @tree_column in pixels. + * + * Returns: The current X offset of @tree_column. + * + * Deprecated: 4.10: Use GtkColumnView instead + */ +int +gtk_tree_view_column_get_x_offset (GtkTreeViewColumn *tree_column) +{ + g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), 0); + + return tree_column->priv->x_offset; +} + +int +_gtk_tree_view_column_request_width (GtkTreeViewColumn *tree_column) +{ + GtkTreeViewColumnPrivate *priv; + int real_requested_width; + + priv = tree_column->priv; + + if (priv->fixed_width != -1) + { + real_requested_width = priv->fixed_width; + } + else if (gtk_tree_view_get_headers_visible (GTK_TREE_VIEW (priv->tree_view))) + { + int button_request; + int requested_width; + + gtk_cell_area_context_get_preferred_width (priv->cell_area_context, &requested_width, NULL); + requested_width += priv->padding; + + gtk_widget_measure (priv->button, GTK_ORIENTATION_HORIZONTAL, -1, + &button_request, NULL, NULL, NULL); + real_requested_width = MAX (requested_width, button_request); + } + else + { + int requested_width; + + gtk_cell_area_context_get_preferred_width (priv->cell_area_context, &requested_width, NULL); + requested_width += priv->padding; + + real_requested_width = requested_width; + if (real_requested_width < 0) + real_requested_width = 0; + } + + if (priv->min_width != -1) + real_requested_width = MAX (real_requested_width, priv->min_width); + + if (priv->max_width != -1) + real_requested_width = MIN (real_requested_width, priv->max_width); + + return real_requested_width; +} + +void +_gtk_tree_view_column_allocate (GtkTreeViewColumn *tree_column, + int x_offset, + int width, + int height) +{ + GtkTreeViewColumnPrivate *priv; + GtkAllocation allocation = { 0, 0, 0, 0 }; + + g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); + + priv = tree_column->priv; + + if (priv->width != width) + gtk_widget_queue_draw (priv->tree_view); + + priv->x_offset = x_offset; + priv->width = width; + + gtk_cell_area_context_allocate (priv->cell_area_context, priv->width - priv->padding, -1); + + if (gtk_tree_view_get_headers_visible (GTK_TREE_VIEW (priv->tree_view))) + { + /* TODO: Underallocates the button horizontally, but + * https://bugzilla.gnome.org/show_bug.cgi?id=770388 + */ + allocation.x = x_offset; + allocation.y = 0; + allocation.width = width; + allocation.height = height; + + gtk_widget_size_allocate (priv->button, &allocation, -1); + } + + g_object_notify_by_pspec (G_OBJECT (tree_column), tree_column_props[PROP_X_OFFSET]); + g_object_notify_by_pspec (G_OBJECT (tree_column), tree_column_props[PROP_WIDTH]); +} + +/** + * gtk_tree_view_column_set_fixed_width: + * @tree_column: A `GtkTreeViewColumn`. + * @fixed_width: The new fixed width, in pixels, or -1. + * + * If @fixed_width is not -1, sets the fixed width of @tree_column; otherwise + * unsets it. The effective value of @fixed_width is clamped between the + * minimum and maximum width of the column; however, the value stored in the + * “fixed-width” property is not clamped. If the column sizing is + * %GTK_TREE_VIEW_COLUMN_GROW_ONLY or %GTK_TREE_VIEW_COLUMN_AUTOSIZE, setting + * a fixed width overrides the automatically calculated width. Note that + * @fixed_width is only a hint to GTK; the width actually allocated to the + * column may be greater or less than requested. + * + * Along with “expand”, the “fixed-width” property changes when the column is + * resized by the user. + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +void +gtk_tree_view_column_set_fixed_width (GtkTreeViewColumn *tree_column, + int fixed_width) +{ + GtkTreeViewColumnPrivate *priv; + + g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); + g_return_if_fail (fixed_width >= -1); + + priv = tree_column->priv; + + if (priv->fixed_width != fixed_width) + { + priv->fixed_width = fixed_width; + if (priv->visible && + priv->tree_view != NULL && + gtk_widget_get_realized (priv->tree_view)) + gtk_widget_queue_resize (priv->tree_view); + + g_object_notify_by_pspec (G_OBJECT (tree_column), tree_column_props[PROP_FIXED_WIDTH]); + } +} + +/** + * gtk_tree_view_column_get_fixed_width: + * @tree_column: A `GtkTreeViewColumn`. + * + * Gets the fixed width of the column. This may not be the actual displayed + * width of the column; for that, use gtk_tree_view_column_get_width(). + * + * Returns: The fixed width of the column. + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +int +gtk_tree_view_column_get_fixed_width (GtkTreeViewColumn *tree_column) +{ + g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), 0); + + return tree_column->priv->fixed_width; +} + +/** + * gtk_tree_view_column_set_min_width: + * @tree_column: A `GtkTreeViewColumn`. + * @min_width: The minimum width of the column in pixels, or -1. + * + * Sets the minimum width of the @tree_column. If @min_width is -1, then the + * minimum width is unset. + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +void +gtk_tree_view_column_set_min_width (GtkTreeViewColumn *tree_column, + int min_width) +{ + GtkTreeViewColumnPrivate *priv; + + g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); + g_return_if_fail (min_width >= -1); + + priv = tree_column->priv; + + if (min_width == priv->min_width) + return; + + if (priv->visible && + priv->tree_view != NULL && + gtk_widget_get_realized (priv->tree_view)) + { + if (min_width > priv->width) + gtk_widget_queue_resize (priv->tree_view); + } + + priv->min_width = min_width; + g_object_freeze_notify (G_OBJECT (tree_column)); + if (priv->max_width != -1 && priv->max_width < min_width) + { + priv->max_width = min_width; + g_object_notify_by_pspec (G_OBJECT (tree_column), tree_column_props[PROP_MAX_WIDTH]); + } + g_object_notify_by_pspec (G_OBJECT (tree_column), tree_column_props[PROP_MIN_WIDTH]); + g_object_thaw_notify (G_OBJECT (tree_column)); + + if (priv->column_type == GTK_TREE_VIEW_COLUMN_AUTOSIZE && priv->tree_view) + _gtk_tree_view_column_autosize (GTK_TREE_VIEW (priv->tree_view), + tree_column); +} + +/** + * gtk_tree_view_column_get_min_width: + * @tree_column: A `GtkTreeViewColumn`. + * + * Returns the minimum width in pixels of the @tree_column, or -1 if no minimum + * width is set. + * + * Returns: The minimum width of the @tree_column. + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +int +gtk_tree_view_column_get_min_width (GtkTreeViewColumn *tree_column) +{ + g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), -1); + + return tree_column->priv->min_width; +} + +/** + * gtk_tree_view_column_set_max_width: + * @tree_column: A `GtkTreeViewColumn`. + * @max_width: The maximum width of the column in pixels, or -1. + * + * Sets the maximum width of the @tree_column. If @max_width is -1, then the + * maximum width is unset. Note, the column can actually be wider than max + * width if it’s the last column in a view. In this case, the column expands to + * fill any extra space. + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +void +gtk_tree_view_column_set_max_width (GtkTreeViewColumn *tree_column, + int max_width) +{ + GtkTreeViewColumnPrivate *priv; + + g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); + g_return_if_fail (max_width >= -1); + + priv = tree_column->priv; + + if (max_width == priv->max_width) + return; + + if (priv->visible && + priv->tree_view != NULL && + gtk_widget_get_realized (priv->tree_view)) + { + if (max_width != -1 && max_width < priv->width) + gtk_widget_queue_resize (priv->tree_view); + } + + priv->max_width = max_width; + g_object_freeze_notify (G_OBJECT (tree_column)); + if (max_width != -1 && max_width < priv->min_width) + { + priv->min_width = max_width; + g_object_notify_by_pspec (G_OBJECT (tree_column), tree_column_props[PROP_MIN_WIDTH]); + } + g_object_notify_by_pspec (G_OBJECT (tree_column), tree_column_props[PROP_MAX_WIDTH]); + g_object_thaw_notify (G_OBJECT (tree_column)); + + if (priv->column_type == GTK_TREE_VIEW_COLUMN_AUTOSIZE && priv->tree_view) + _gtk_tree_view_column_autosize (GTK_TREE_VIEW (priv->tree_view), + tree_column); +} + +/** + * gtk_tree_view_column_get_max_width: + * @tree_column: A `GtkTreeViewColumn`. + * + * Returns the maximum width in pixels of the @tree_column, or -1 if no maximum + * width is set. + * + * Returns: The maximum width of the @tree_column. + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +int +gtk_tree_view_column_get_max_width (GtkTreeViewColumn *tree_column) +{ + g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), -1); + + return tree_column->priv->max_width; +} + +/** + * gtk_tree_view_column_clicked: + * @tree_column: a `GtkTreeViewColumn` + * + * Emits the “clicked” signal on the column. This function will only work if + * @tree_column is clickable. + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +void +gtk_tree_view_column_clicked (GtkTreeViewColumn *tree_column) +{ + GtkTreeViewColumnPrivate *priv; + + g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); + + priv = tree_column->priv; + + if (priv->visible && priv->clickable) + g_signal_emit_by_name (priv->button, "clicked"); +} + +/** + * gtk_tree_view_column_set_title: + * @tree_column: A `GtkTreeViewColumn`. + * @title: The title of the @tree_column. + * + * Sets the title of the @tree_column. If a custom widget has been set, then + * this value is ignored. + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +void +gtk_tree_view_column_set_title (GtkTreeViewColumn *tree_column, + const char *title) +{ + GtkTreeViewColumnPrivate *priv; + char *new_title; + + g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); + + priv = tree_column->priv; + + new_title = g_strdup (title); + g_free (priv->title); + priv->title = new_title; + + gtk_tree_view_column_update_button (tree_column); + g_object_notify_by_pspec (G_OBJECT (tree_column), tree_column_props[PROP_TITLE]); +} + +/** + * gtk_tree_view_column_get_title: + * @tree_column: A `GtkTreeViewColumn`. + * + * Returns the title of the widget. + * + * Returns: the title of the column. This string should not be + * modified or freed. + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +const char * +gtk_tree_view_column_get_title (GtkTreeViewColumn *tree_column) +{ + g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), NULL); + + return tree_column->priv->title; +} + +/** + * gtk_tree_view_column_set_expand: + * @tree_column: A `GtkTreeViewColumn`. + * @expand: %TRUE if the column should expand to fill available space. + * + * Sets the column to take available extra space. This space is shared equally + * amongst all columns that have the expand set to %TRUE. If no column has this + * option set, then the last column gets all extra space. By default, every + * column is created with this %FALSE. + * + * Along with “fixed-width”, the “expand” property changes when the column is + * resized by the user. + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +void +gtk_tree_view_column_set_expand (GtkTreeViewColumn *tree_column, + gboolean expand) +{ + GtkTreeViewColumnPrivate *priv; + + g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); + + priv = tree_column->priv; + + expand = expand?TRUE:FALSE; + if (priv->expand == expand) + return; + priv->expand = expand; + + if (priv->visible && + priv->tree_view != NULL && + gtk_widget_get_realized (priv->tree_view)) + { + gtk_widget_queue_resize (priv->tree_view); + } + + g_object_notify_by_pspec (G_OBJECT (tree_column), tree_column_props[PROP_EXPAND]); +} + +/** + * gtk_tree_view_column_get_expand: + * @tree_column: A `GtkTreeViewColumn`. + * + * Returns %TRUE if the column expands to fill available space. + * + * Returns: %TRUE if the column expands to fill available space. + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +gboolean +gtk_tree_view_column_get_expand (GtkTreeViewColumn *tree_column) +{ + g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), FALSE); + + return tree_column->priv->expand; +} + +/** + * gtk_tree_view_column_set_clickable: + * @tree_column: A `GtkTreeViewColumn`. + * @clickable: %TRUE if the header is active. + * + * Sets the header to be active if @clickable is %TRUE. When the header is + * active, then it can take keyboard focus, and can be clicked. + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +void +gtk_tree_view_column_set_clickable (GtkTreeViewColumn *tree_column, + gboolean clickable) +{ + GtkTreeViewColumnPrivate *priv; + + g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); + + priv = tree_column->priv; + + clickable = !! clickable; + if (priv->clickable == clickable) + return; + + priv->clickable = clickable; + gtk_tree_view_column_update_button (tree_column); + g_object_notify_by_pspec (G_OBJECT (tree_column), tree_column_props[PROP_CLICKABLE]); +} + +/** + * gtk_tree_view_column_get_clickable: + * @tree_column: a `GtkTreeViewColumn` + * + * Returns %TRUE if the user can click on the header for the column. + * + * Returns: %TRUE if user can click the column header. + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +gboolean +gtk_tree_view_column_get_clickable (GtkTreeViewColumn *tree_column) +{ + g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), FALSE); + + return tree_column->priv->clickable; +} + +/** + * gtk_tree_view_column_set_widget: + * @tree_column: A `GtkTreeViewColumn`. + * @widget: (nullable): A child `GtkWidget` + * + * Sets the widget in the header to be @widget. If widget is %NULL, then the + * header button is set with a `GtkLabel` set to the title of @tree_column. + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +void +gtk_tree_view_column_set_widget (GtkTreeViewColumn *tree_column, + GtkWidget *widget) +{ + GtkTreeViewColumnPrivate *priv; + + g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); + g_return_if_fail (widget == NULL || GTK_IS_WIDGET (widget)); + + priv = tree_column->priv; + + if (widget) + g_object_ref_sink (widget); + + if (priv->child) + g_object_unref (priv->child); + + priv->child = widget; + gtk_tree_view_column_update_button (tree_column); + g_object_notify_by_pspec (G_OBJECT (tree_column), tree_column_props[PROP_WIDGET]); +} + +/** + * gtk_tree_view_column_get_widget: + * @tree_column: A `GtkTreeViewColumn` + * + * Returns the `GtkWidget` in the button on the column header. + * + * If a custom widget has not been set then %NULL is returned. + * + * Returns: (nullable) (transfer none): The `GtkWidget` in the column header + * + * Deprecated: 4.10: Use GtkColumnView instead + */ +GtkWidget * +gtk_tree_view_column_get_widget (GtkTreeViewColumn *tree_column) +{ + g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), NULL); + + return tree_column->priv->child; +} + +/** + * gtk_tree_view_column_set_alignment: + * @tree_column: A `GtkTreeViewColumn`. + * @xalign: The alignment, which is between [0.0 and 1.0] inclusive. + * + * Sets the alignment of the title or custom widget inside the column header. + * The alignment determines its location inside the button -- 0.0 for left, 0.5 + * for center, 1.0 for right. + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +void +gtk_tree_view_column_set_alignment (GtkTreeViewColumn *tree_column, + float xalign) +{ + GtkTreeViewColumnPrivate *priv; + + g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); + + priv = tree_column->priv; + + xalign = CLAMP (xalign, 0.0, 1.0); + + if (priv->xalign == xalign) + return; + + priv->xalign = xalign; + gtk_tree_view_column_update_button (tree_column); + g_object_notify_by_pspec (G_OBJECT (tree_column), tree_column_props[PROP_ALIGNMENT]); +} + +/** + * gtk_tree_view_column_get_alignment: + * @tree_column: A `GtkTreeViewColumn`. + * + * Returns the current x alignment of @tree_column. This value can range + * between 0.0 and 1.0. + * + * Returns: The current alignent of @tree_column. + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +float +gtk_tree_view_column_get_alignment (GtkTreeViewColumn *tree_column) +{ + g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), 0.5); + + return tree_column->priv->xalign; +} + +/** + * gtk_tree_view_column_set_reorderable: + * @tree_column: A `GtkTreeViewColumn` + * @reorderable: %TRUE, if the column can be reordered. + * + * If @reorderable is %TRUE, then the column can be reordered by the end user + * dragging the header. + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +void +gtk_tree_view_column_set_reorderable (GtkTreeViewColumn *tree_column, + gboolean reorderable) +{ + GtkTreeViewColumnPrivate *priv; + + g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); + + priv = tree_column->priv; + + /* if (reorderable) + gtk_tree_view_column_set_clickable (tree_column, TRUE);*/ + + if (priv->reorderable == (reorderable?TRUE:FALSE)) + return; + + priv->reorderable = (reorderable?TRUE:FALSE); + gtk_tree_view_column_update_button (tree_column); + g_object_notify_by_pspec (G_OBJECT (tree_column), tree_column_props[PROP_REORDERABLE]); +} + +/** + * gtk_tree_view_column_get_reorderable: + * @tree_column: A `GtkTreeViewColumn` + * + * Returns %TRUE if the @tree_column can be reordered by the user. + * + * Returns: %TRUE if the @tree_column can be reordered by the user. + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +gboolean +gtk_tree_view_column_get_reorderable (GtkTreeViewColumn *tree_column) +{ + g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), FALSE); + + return tree_column->priv->reorderable; +} + + +/** + * gtk_tree_view_column_set_sort_column_id: + * @tree_column: a `GtkTreeViewColumn` + * @sort_column_id: The @sort_column_id of the model to sort on. + * + * Sets the logical @sort_column_id that this column sorts on when this column + * is selected for sorting. Doing so makes the column header clickable. + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +void +gtk_tree_view_column_set_sort_column_id (GtkTreeViewColumn *tree_column, + int sort_column_id) +{ + GtkTreeViewColumnPrivate *priv; + + g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); + g_return_if_fail (sort_column_id >= -1); + + priv = tree_column->priv; + + if (priv->sort_column_id == sort_column_id) + return; + + priv->sort_column_id = sort_column_id; + + /* Handle unsetting the id */ + if (sort_column_id == -1) + { + GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree_view)); + + if (priv->sort_clicked_signal) + { + g_signal_handler_disconnect (tree_column, priv->sort_clicked_signal); + priv->sort_clicked_signal = 0; + } + + if (priv->sort_column_changed_signal) + { + g_signal_handler_disconnect (model, priv->sort_column_changed_signal); + priv->sort_column_changed_signal = 0; + } + + gtk_tree_view_column_set_sort_order (tree_column, GTK_SORT_ASCENDING); + gtk_tree_view_column_set_sort_indicator (tree_column, FALSE); + gtk_tree_view_column_set_clickable (tree_column, FALSE); + g_object_notify_by_pspec (G_OBJECT (tree_column), tree_column_props[PROP_SORT_COLUMN_ID]); + return; + } + + gtk_tree_view_column_set_clickable (tree_column, TRUE); + + if (! priv->sort_clicked_signal) + priv->sort_clicked_signal = g_signal_connect (tree_column, + "clicked", + G_CALLBACK (gtk_tree_view_column_sort), + NULL); + + gtk_tree_view_column_setup_sort_column_id_callback (tree_column); + g_object_notify_by_pspec (G_OBJECT (tree_column), tree_column_props[PROP_SORT_COLUMN_ID]); +} + +/** + * gtk_tree_view_column_get_sort_column_id: + * @tree_column: a `GtkTreeViewColumn` + * + * Gets the logical @sort_column_id that the model sorts on + * when this column is selected for sorting. + * + * See [method@Gtk.TreeViewColumn.set_sort_column_id]. + * + * Returns: the current @sort_column_id for this column, or -1 if + * this column can’t be used for sorting + * + * Deprecated: 4.10: Use GtkColumnView instead + */ +int +gtk_tree_view_column_get_sort_column_id (GtkTreeViewColumn *tree_column) +{ + g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), 0); + + return tree_column->priv->sort_column_id; +} + +/** + * gtk_tree_view_column_set_sort_indicator: + * @tree_column: a `GtkTreeViewColumn` + * @setting: %TRUE to display an indicator that the column is sorted + * + * Call this function with a @setting of %TRUE to display an arrow in + * the header button indicating the column is sorted. Call + * gtk_tree_view_column_set_sort_order() to change the direction of + * the arrow. + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +void +gtk_tree_view_column_set_sort_indicator (GtkTreeViewColumn *tree_column, + gboolean setting) +{ + g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); + + setting = setting != FALSE; + + if (setting == tree_column->priv->show_sort_indicator) + return; + + tree_column->priv->show_sort_indicator = setting; + gtk_tree_view_column_update_button (tree_column); + g_object_notify_by_pspec (G_OBJECT (tree_column), tree_column_props[PROP_SORT_INDICATOR]); +} + +/** + * gtk_tree_view_column_get_sort_indicator: + * @tree_column: a `GtkTreeViewColumn` + * + * Gets the value set by gtk_tree_view_column_set_sort_indicator(). + * + * Returns: whether the sort indicator arrow is displayed + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +gboolean +gtk_tree_view_column_get_sort_indicator (GtkTreeViewColumn *tree_column) +{ + g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), FALSE); + + return tree_column->priv->show_sort_indicator; +} + +/** + * gtk_tree_view_column_set_sort_order: + * @tree_column: a `GtkTreeViewColumn` + * @order: sort order that the sort indicator should indicate + * + * Changes the appearance of the sort indicator. + * + * This does not actually sort the model. Use + * gtk_tree_view_column_set_sort_column_id() if you want automatic sorting + * support. This function is primarily for custom sorting behavior, and should + * be used in conjunction with gtk_tree_sortable_set_sort_column_id() to do + * that. For custom models, the mechanism will vary. + * + * The sort indicator changes direction to indicate normal sort or reverse sort. + * Note that you must have the sort indicator enabled to see anything when + * calling this function; see gtk_tree_view_column_set_sort_indicator(). + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +void +gtk_tree_view_column_set_sort_order (GtkTreeViewColumn *tree_column, + GtkSortType order) +{ + g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); + + if (order == tree_column->priv->sort_order) + return; + + tree_column->priv->sort_order = order; + gtk_tree_view_column_update_button (tree_column); + g_object_notify_by_pspec (G_OBJECT (tree_column), tree_column_props[PROP_SORT_ORDER]); +} + +/** + * gtk_tree_view_column_get_sort_order: + * @tree_column: a `GtkTreeViewColumn` + * + * Gets the value set by gtk_tree_view_column_set_sort_order(). + * + * Returns: the sort order the sort indicator is indicating + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +GtkSortType +gtk_tree_view_column_get_sort_order (GtkTreeViewColumn *tree_column) +{ + g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), 0); + + return tree_column->priv->sort_order; +} + +/** + * gtk_tree_view_column_cell_set_cell_data: + * @tree_column: A `GtkTreeViewColumn`. + * @tree_model: The `GtkTreeModel` to get the cell renderers attributes from. + * @iter: The `GtkTreeIter` to get the cell renderer’s attributes from. + * @is_expander: %TRUE, if the row has children + * @is_expanded: %TRUE, if the row has visible children + * + * Sets the cell renderer based on the @tree_model and @iter. That is, for + * every attribute mapping in @tree_column, it will get a value from the set + * column on the @iter, and use that value to set the attribute on the cell + * renderer. This is used primarily by the `GtkTreeView`. + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +void +gtk_tree_view_column_cell_set_cell_data (GtkTreeViewColumn *tree_column, + GtkTreeModel *tree_model, + GtkTreeIter *iter, + gboolean is_expander, + gboolean is_expanded) +{ + g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); + + if (tree_model == NULL) + return; + + gtk_cell_area_apply_attributes (tree_column->priv->cell_area, tree_model, iter, + is_expander, is_expanded); +} + +/** + * gtk_tree_view_column_cell_get_size: + * @tree_column: A `GtkTreeViewColumn`. + * @x_offset: (out) (optional): location to return x offset of a cell relative to @cell_area + * @y_offset: (out) (optional): location to return y offset of a cell relative to @cell_area + * @width: (out) (optional): location to return width needed to render a cell + * @height: (out) (optional): location to return height needed to render a cell + * + * Obtains the width and height needed to render the column. This is used + * primarily by the `GtkTreeView`. + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +void +gtk_tree_view_column_cell_get_size (GtkTreeViewColumn *tree_column, + int *x_offset, + int *y_offset, + int *width, + int *height) +{ + GtkTreeViewColumnPrivate *priv; + int min_width = 0, min_height = 0; + + g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); + + priv = tree_column->priv; + + g_signal_handler_block (priv->cell_area_context, + priv->context_changed_signal); + + gtk_cell_area_get_preferred_width (priv->cell_area, + priv->cell_area_context, + priv->tree_view, + NULL, NULL); + + gtk_cell_area_context_get_preferred_width (priv->cell_area_context, &min_width, NULL); + + gtk_cell_area_get_preferred_height_for_width (priv->cell_area, + priv->cell_area_context, + priv->tree_view, + min_width, + &min_height, + NULL); + + g_signal_handler_unblock (priv->cell_area_context, + priv->context_changed_signal); + + + if (height) + * height = min_height; + if (width) + * width = min_width; + +} + +/** + * gtk_tree_view_column_cell_snapshot: + * @tree_column: A `GtkTreeViewColumn`. + * @snapshot: `GtkSnapshot` to draw to + * @background_area: entire cell area (including tree expanders and maybe padding on the sides) + * @cell_area: area normally rendered by a cell renderer + * @flags: flags that affect rendering + * + * Renders the cell contained by #tree_column. This is used primarily by the + * `GtkTreeView`. + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +void +gtk_tree_view_column_cell_snapshot (GtkTreeViewColumn *tree_column, + GtkSnapshot *snapshot, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + guint flags, + gboolean draw_focus) +{ + GtkTreeViewColumnPrivate *priv; + + g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); + g_return_if_fail (snapshot != NULL); + g_return_if_fail (background_area != NULL); + g_return_if_fail (cell_area != NULL); + + priv = tree_column->priv; + + gtk_cell_area_snapshot (priv->cell_area, priv->cell_area_context, + priv->tree_view, snapshot, + background_area, cell_area, flags, + draw_focus); +} + +gboolean +_gtk_tree_view_column_cell_event (GtkTreeViewColumn *tree_column, + GdkEvent *event, + const GdkRectangle *cell_area, + guint flags) +{ + GtkTreeViewColumnPrivate *priv; + + g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), FALSE); + + priv = tree_column->priv; + + return gtk_cell_area_event (priv->cell_area, + priv->cell_area_context, + priv->tree_view, + event, + cell_area, + flags); +} + +/** + * gtk_tree_view_column_cell_is_visible: + * @tree_column: A `GtkTreeViewColumn` + * + * Returns %TRUE if any of the cells packed into the @tree_column are visible. + * For this to be meaningful, you must first initialize the cells with + * gtk_tree_view_column_cell_set_cell_data() + * + * Returns: %TRUE, if any of the cells packed into the @tree_column are currently visible + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +gboolean +gtk_tree_view_column_cell_is_visible (GtkTreeViewColumn *tree_column) +{ + GList *list; + GList *cells; + GtkTreeViewColumnPrivate *priv; + + g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), FALSE); + + priv = tree_column->priv; + + cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (priv->cell_area)); + for (list = cells; list; list = list->next) + { + if (gtk_cell_renderer_get_visible (list->data)) + { + g_list_free (cells); + return TRUE; + } + } + + g_list_free (cells); + + return FALSE; +} + +/** + * gtk_tree_view_column_focus_cell: + * @tree_column: A `GtkTreeViewColumn` + * @cell: A `GtkCellRenderer` + * + * Sets the current keyboard focus to be at @cell, if the column contains + * 2 or more editable and activatable cells. + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +void +gtk_tree_view_column_focus_cell (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell) +{ + g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); + g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); + + gtk_cell_area_set_focus_cell (tree_column->priv->cell_area, cell); +} + +void +_gtk_tree_view_column_cell_set_dirty (GtkTreeViewColumn *tree_column, + gboolean install_handler) +{ + GtkTreeViewColumnPrivate *priv = tree_column->priv; + + priv->dirty = TRUE; + priv->padding = 0; + priv->width = 0; + + /* Issue a manual reset on the context to have all + * sizes re-requested for the context. + */ + g_signal_handler_block (priv->cell_area_context, + priv->context_changed_signal); + gtk_cell_area_context_reset (priv->cell_area_context); + g_signal_handler_unblock (priv->cell_area_context, + priv->context_changed_signal); + + if (priv->tree_view && + gtk_widget_get_realized (priv->tree_view)) + { + _gtk_tree_view_install_mark_rows_col_dirty (GTK_TREE_VIEW (priv->tree_view), install_handler); + gtk_widget_queue_resize (priv->tree_view); + } +} + +gboolean +_gtk_tree_view_column_cell_get_dirty (GtkTreeViewColumn *tree_column) +{ + return tree_column->priv->dirty; +} + +/** + * gtk_tree_view_column_cell_get_position: + * @tree_column: a `GtkTreeViewColumn` + * @cell_renderer: a `GtkCellRenderer` + * @x_offset: (out) (optional): return location for the horizontal + * position of @cell within @tree_column + * @width: (out) (optional): return location for the width of @cell + * + * Obtains the horizontal position and size of a cell in a column. + * + * If the cell is not found in the column, @start_pos and @width + * are not changed and %FALSE is returned. + * + * Returns: %TRUE if @cell belongs to @tree_column + * + * Deprecated: 4.10: Use GtkColumnView instead + */ +gboolean +gtk_tree_view_column_cell_get_position (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell_renderer, + int *x_offset, + int *width) +{ + GtkTreeViewColumnPrivate *priv; + GdkRectangle cell_area; + GdkRectangle allocation; + + g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), FALSE); + g_return_val_if_fail (GTK_IS_CELL_RENDERER (cell_renderer), FALSE); + + priv = tree_column->priv; + + if (! gtk_cell_area_has_renderer (priv->cell_area, cell_renderer)) + return FALSE; + + gtk_tree_view_get_background_area (GTK_TREE_VIEW (priv->tree_view), + NULL, tree_column, &cell_area); + + gtk_cell_area_get_cell_allocation (priv->cell_area, + priv->cell_area_context, + priv->tree_view, + cell_renderer, + &cell_area, + &allocation); + + if (x_offset) + *x_offset = allocation.x - cell_area.x; + + if (width) + *width = allocation.width; + + return TRUE; +} + +/** + * gtk_tree_view_column_queue_resize: + * @tree_column: A `GtkTreeViewColumn` + * + * Flags the column, and the cell renderers added to this column, to have + * their sizes renegotiated. + * + * Deprecated: 4.10: Use GtkColumnView instead + **/ +void +gtk_tree_view_column_queue_resize (GtkTreeViewColumn *tree_column) +{ + g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); + + if (tree_column->priv->tree_view) + _gtk_tree_view_column_cell_set_dirty (tree_column, TRUE); +} + +/** + * gtk_tree_view_column_get_tree_view: + * @tree_column: A `GtkTreeViewColumn` + * + * Returns the `GtkTreeView` wherein @tree_column has been inserted. + * If @column is currently not inserted in any tree view, %NULL is + * returned. + * + * Returns: (nullable) (transfer none): The tree view wherein @column + * has been inserted + * + * Deprecated: 4.10: Use GtkColumnView instead + */ +GtkWidget * +gtk_tree_view_column_get_tree_view (GtkTreeViewColumn *tree_column) +{ + g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), NULL); + + return tree_column->priv->tree_view; +} + +/** + * gtk_tree_view_column_get_button: + * @tree_column: A `GtkTreeViewColumn` + * + * Returns the button used in the treeview column header + * + * Returns: (transfer none): The button for the column header. + * + * Deprecated: 4.10: Use GtkColumnView instead + */ +GtkWidget * +gtk_tree_view_column_get_button (GtkTreeViewColumn *tree_column) +{ + g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), NULL); + + return tree_column->priv->button; +} + +void +_gtk_tree_view_column_push_padding (GtkTreeViewColumn *column, + int padding) +{ + column->priv->padding = MAX (column->priv->padding, padding); +} + +int +_gtk_tree_view_column_get_requested_width (GtkTreeViewColumn *column) +{ + int requested_width; + + gtk_cell_area_context_get_preferred_width (column->priv->cell_area_context, &requested_width, NULL); + + return requested_width + column->priv->padding; +} + +int +_gtk_tree_view_column_get_drag_x (GtkTreeViewColumn *column) +{ + return column->priv->drag_x; +} + +GtkCellAreaContext * +_gtk_tree_view_column_get_context (GtkTreeViewColumn *column) +{ + return column->priv->cell_area_context; +} + +gboolean +_gtk_tree_view_column_coords_in_resize_rect (GtkTreeViewColumn *column, + double x, + double y) +{ + GtkTreeViewColumnPrivate *priv = column->priv; + graphene_rect_t button_bounds; + + /* x and y are in treeview coordinates. */ + + if (!gtk_widget_get_realized (priv->button) || + !priv->resizable || + !priv->visible) + return FALSE; + + if (!gtk_widget_compute_bounds (priv->button, priv->tree_view, &button_bounds)) + return FALSE; + + if (gtk_widget_get_direction (priv->tree_view) == GTK_TEXT_DIR_LTR) + button_bounds.origin.x += button_bounds.size.width - TREE_VIEW_DRAG_WIDTH; + + button_bounds.size.width = TREE_VIEW_DRAG_WIDTH; + + return graphene_rect_contains_point (&button_bounds, + &(graphene_point_t){x, y}); +} diff --git a/gtk/deprecated/gtktreeviewcolumn.h b/gtk/deprecated/gtktreeviewcolumn.h new file mode 100644 index 0000000000..21f1c22582 --- /dev/null +++ b/gtk/deprecated/gtktreeviewcolumn.h @@ -0,0 +1,254 @@ +/* gtktreeviewcolumn.h + * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#ifndef __GTK_TREE_VIEW_COLUMN_H__ +#define __GTK_TREE_VIEW_COLUMN_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include +#include +#include + + +G_BEGIN_DECLS + + +#define GTK_TYPE_TREE_VIEW_COLUMN (gtk_tree_view_column_get_type ()) +#define GTK_TREE_VIEW_COLUMN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_TREE_VIEW_COLUMN, GtkTreeViewColumn)) +#define GTK_IS_TREE_VIEW_COLUMN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_TREE_VIEW_COLUMN)) + +typedef struct _GtkTreeViewColumn GtkTreeViewColumn; + +/** + * GtkTreeViewColumnSizing: + * @GTK_TREE_VIEW_COLUMN_GROW_ONLY: Columns only get bigger in reaction to changes in the model + * @GTK_TREE_VIEW_COLUMN_AUTOSIZE: Columns resize to be the optimal size every time the model changes. + * @GTK_TREE_VIEW_COLUMN_FIXED: Columns are a fixed numbers of pixels wide. + * + * The sizing method the column uses to determine its width. Please note + * that %GTK_TREE_VIEW_COLUMN_AUTOSIZE are inefficient for large views, and + * can make columns appear choppy. + */ +typedef enum +{ + GTK_TREE_VIEW_COLUMN_GROW_ONLY, + GTK_TREE_VIEW_COLUMN_AUTOSIZE, + GTK_TREE_VIEW_COLUMN_FIXED +} GtkTreeViewColumnSizing; + +/** + * GtkTreeCellDataFunc: + * @tree_column: A `GtkTreeViewColumn` + * @cell: The `GtkCellRenderer` that is being rendered by @tree_column + * @tree_model: The `GtkTreeModel` being rendered + * @iter: A `GtkTreeIter` of the current row rendered + * @data: (closure): user data + * + * A function to set the properties of a cell instead of just using the + * straight mapping between the cell and the model. + * + * This function is useful for customizing the cell renderer. For example, + * a function might get an* integer from the @tree_model, and render it to + * the “text” attribute of “cell” by converting it to its written equivalent. + * + * See also: gtk_tree_view_column_set_cell_data_func() + */ +typedef void (* GtkTreeCellDataFunc) (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *tree_model, + GtkTreeIter *iter, + gpointer data); + + +GDK_AVAILABLE_IN_ALL +GType gtk_tree_view_column_get_type (void) G_GNUC_CONST; +GDK_DEPRECATED_IN_4_10 +GtkTreeViewColumn *gtk_tree_view_column_new (void); +GDK_DEPRECATED_IN_4_10 +GtkTreeViewColumn *gtk_tree_view_column_new_with_area (GtkCellArea *area); +GDK_DEPRECATED_IN_4_10 +GtkTreeViewColumn *gtk_tree_view_column_new_with_attributes (const char *title, + GtkCellRenderer *cell, + ...) G_GNUC_NULL_TERMINATED; +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_column_pack_start (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + gboolean expand); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_column_pack_end (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + gboolean expand); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_column_clear (GtkTreeViewColumn *tree_column); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_column_add_attribute (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell_renderer, + const char *attribute, + int column); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_column_set_attributes (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell_renderer, + ...) G_GNUC_NULL_TERMINATED; +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_column_set_cell_data_func (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell_renderer, + GtkTreeCellDataFunc func, + gpointer func_data, + GDestroyNotify destroy); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_column_clear_attributes (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell_renderer); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_column_set_spacing (GtkTreeViewColumn *tree_column, + int spacing); +GDK_DEPRECATED_IN_4_10 +int gtk_tree_view_column_get_spacing (GtkTreeViewColumn *tree_column); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_column_set_visible (GtkTreeViewColumn *tree_column, + gboolean visible); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_view_column_get_visible (GtkTreeViewColumn *tree_column); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_column_set_resizable (GtkTreeViewColumn *tree_column, + gboolean resizable); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_view_column_get_resizable (GtkTreeViewColumn *tree_column); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_column_set_sizing (GtkTreeViewColumn *tree_column, + GtkTreeViewColumnSizing type); +GDK_DEPRECATED_IN_4_10 +GtkTreeViewColumnSizing gtk_tree_view_column_get_sizing (GtkTreeViewColumn *tree_column); +GDK_DEPRECATED_IN_4_10 +int gtk_tree_view_column_get_x_offset (GtkTreeViewColumn *tree_column); +GDK_DEPRECATED_IN_4_10 +int gtk_tree_view_column_get_width (GtkTreeViewColumn *tree_column); +GDK_DEPRECATED_IN_4_10 +int gtk_tree_view_column_get_fixed_width (GtkTreeViewColumn *tree_column); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_column_set_fixed_width (GtkTreeViewColumn *tree_column, + int fixed_width); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_column_set_min_width (GtkTreeViewColumn *tree_column, + int min_width); +GDK_DEPRECATED_IN_4_10 +int gtk_tree_view_column_get_min_width (GtkTreeViewColumn *tree_column); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_column_set_max_width (GtkTreeViewColumn *tree_column, + int max_width); +GDK_DEPRECATED_IN_4_10 +int gtk_tree_view_column_get_max_width (GtkTreeViewColumn *tree_column); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_column_clicked (GtkTreeViewColumn *tree_column); + + + +/* Options for manipulating the column headers + */ +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_column_set_title (GtkTreeViewColumn *tree_column, + const char *title); +GDK_DEPRECATED_IN_4_10 +const char * gtk_tree_view_column_get_title (GtkTreeViewColumn *tree_column); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_column_set_expand (GtkTreeViewColumn *tree_column, + gboolean expand); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_view_column_get_expand (GtkTreeViewColumn *tree_column); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_column_set_clickable (GtkTreeViewColumn *tree_column, + gboolean clickable); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_view_column_get_clickable (GtkTreeViewColumn *tree_column); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_column_set_widget (GtkTreeViewColumn *tree_column, + GtkWidget *widget); +GDK_DEPRECATED_IN_4_10 +GtkWidget *gtk_tree_view_column_get_widget (GtkTreeViewColumn *tree_column); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_column_set_alignment (GtkTreeViewColumn *tree_column, + float xalign); +GDK_DEPRECATED_IN_4_10 +float gtk_tree_view_column_get_alignment (GtkTreeViewColumn *tree_column); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_column_set_reorderable (GtkTreeViewColumn *tree_column, + gboolean reorderable); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_view_column_get_reorderable (GtkTreeViewColumn *tree_column); + + + +/* You probably only want to use gtk_tree_view_column_set_sort_column_id. The + * other sorting functions exist primarily to let others do their own custom sorting. + */ +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_column_set_sort_column_id (GtkTreeViewColumn *tree_column, + int sort_column_id); +GDK_DEPRECATED_IN_4_10 +int gtk_tree_view_column_get_sort_column_id (GtkTreeViewColumn *tree_column); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_column_set_sort_indicator (GtkTreeViewColumn *tree_column, + gboolean setting); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_view_column_get_sort_indicator (GtkTreeViewColumn *tree_column); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_column_set_sort_order (GtkTreeViewColumn *tree_column, + GtkSortType order); +GDK_DEPRECATED_IN_4_10 +GtkSortType gtk_tree_view_column_get_sort_order (GtkTreeViewColumn *tree_column); + + +/* These functions are meant primarily for interaction between the GtkTreeView and the column. + */ +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_column_cell_set_cell_data (GtkTreeViewColumn *tree_column, + GtkTreeModel *tree_model, + GtkTreeIter *iter, + gboolean is_expander, + gboolean is_expanded); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_column_cell_get_size (GtkTreeViewColumn *tree_column, + int *x_offset, + int *y_offset, + int *width, + int *height); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_view_column_cell_is_visible (GtkTreeViewColumn *tree_column); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_column_focus_cell (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell); +GDK_DEPRECATED_IN_4_10 +gboolean gtk_tree_view_column_cell_get_position (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell_renderer, + int *x_offset, + int *width); +GDK_DEPRECATED_IN_4_10 +void gtk_tree_view_column_queue_resize (GtkTreeViewColumn *tree_column); +GDK_DEPRECATED_IN_4_10 +GtkWidget *gtk_tree_view_column_get_tree_view (GtkTreeViewColumn *tree_column); +GDK_DEPRECATED_IN_4_10 +GtkWidget *gtk_tree_view_column_get_button (GtkTreeViewColumn *tree_column); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkTreeViewColumn, g_object_unref) + +G_END_DECLS + + +#endif /* __GTK_TREE_VIEW_COLUMN_H__ */ diff --git a/gtk/deprecated/meson.build b/gtk/deprecated/meson.build index 96b38bb53d..655bd4b10e 100644 --- a/gtk/deprecated/meson.build +++ b/gtk/deprecated/meson.build @@ -3,7 +3,39 @@ gtk_deprecated_sources = [ 'deprecated/gtkappchooserbutton.c', 'deprecated/gtkappchooserdialog.c', 'deprecated/gtkappchooserwidget.c', + 'deprecated/gtkcellarea.c', + 'deprecated/gtkcellareabox.c', + 'deprecated/gtkcellareacontext.c', + 'deprecated/gtkcellareaboxcontext.c', + 'deprecated/gtkcelleditable.c', + 'deprecated/gtkcelllayout.c', + 'deprecated/gtkcellrenderer.c', + 'deprecated/gtkcellrendereraccel.c', + 'deprecated/gtkcellrenderercombo.c', + 'deprecated/gtkcellrendererpixbuf.c', + 'deprecated/gtkcellrendererprogress.c', + 'deprecated/gtkcellrendererspin.c', + 'deprecated/gtkcellrendererspinner.c', + 'deprecated/gtkcellrenderertext.c', + 'deprecated/gtkcellrenderertoggle.c', + 'deprecated/gtkcellview.c', + 'deprecated/gtkcombobox.c', + 'deprecated/gtkcomboboxtext.c', 'deprecated/gtkentrycompletion.c', + 'deprecated/gtkiconview.c', + 'deprecated/gtkliststore.c', + 'deprecated/gtktreedatalist.c', + 'deprecated/gtktreednd.c', + 'deprecated/gtktreemodel.c', + 'deprecated/gtktreemodelfilter.c', + 'deprecated/gtktreemodelsort.c', + 'deprecated/gtktreerbtree.c', + 'deprecated/gtktreeselection.c', + 'deprecated/gtktreesortable.c', + 'deprecated/gtktreestore.c', + 'deprecated/gtktreepopover.c', + 'deprecated/gtktreeview.c', + 'deprecated/gtktreeviewcolumn.c', ] gtk_deprecated_headers = [ @@ -11,5 +43,33 @@ gtk_deprecated_headers = [ 'deprecated/gtkappchooserbutton.h', 'deprecated/gtkappchooserdialog.h', 'deprecated/gtkappchooserwidget.h', + 'deprecated/gtkcellarea.h', + 'deprecated/gtkcellareabox.h', + 'deprecated/gtkcellareacontext.h', + 'deprecated/gtkcelleditable.h', + 'deprecated/gtkcelllayout.h', + 'deprecated/gtkcellrenderer.h', + 'deprecated/gtkcellrendereraccel.h', + 'deprecated/gtkcellrenderercombo.h', + 'deprecated/gtkcellrendererpixbuf.h', + 'deprecated/gtkcellrendererprogress.h', + 'deprecated/gtkcellrendererspin.h', + 'deprecated/gtkcellrendererspinner.h', + 'deprecated/gtkcellrenderertext.h', + 'deprecated/gtkcellrenderertoggle.h', + 'deprecated/gtkcellview.h', + 'deprecated/gtkcombobox.h', + 'deprecated/gtkcomboboxtext.h', 'deprecated/gtkentrycompletion.h', + 'deprecated/gtkiconview.h', + 'deprecated/gtkliststore.h', + 'deprecated/gtktreednd.h', + 'deprecated/gtktreemodel.h', + 'deprecated/gtktreemodelfilter.h', + 'deprecated/gtktreemodelsort.h', + 'deprecated/gtktreeselection.h', + 'deprecated/gtktreesortable.h', + 'deprecated/gtktreestore.h', + 'deprecated/gtktreeview.h', + 'deprecated/gtktreeviewcolumn.h', ] diff --git a/gtk/gtk.h b/gtk/gtk.h index 0451ad1591..d4a4b166d1 100644 --- a/gtk/gtk.h +++ b/gtk/gtk.h @@ -60,21 +60,21 @@ #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include @@ -85,8 +85,8 @@ #include #include #include -#include -#include +#include +#include #include #include #include @@ -150,7 +150,7 @@ #include #include #include -#include +#include #include #include #include @@ -166,7 +166,7 @@ #include #include #include -#include +#include #include #include #include @@ -263,18 +263,18 @@ #include #include #include -#include +#include #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include diff --git a/gtk/gtkcellarea.c b/gtk/gtkcellarea.c deleted file mode 100644 index 5d4f0c3eaf..0000000000 --- a/gtk/gtkcellarea.c +++ /dev/null @@ -1,3552 +0,0 @@ -/* gtkcellarea.c - * - * Copyright (C) 2010 Openismus GmbH - * - * Authors: - * Tristan Van Berkom - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -/** - * GtkCellArea: - * - * An abstract class for laying out `GtkCellRenderer`s - * - * The `GtkCellArea` is an abstract class for [iface@Gtk.CellLayout] - * widgets (also referred to as "layouting widgets") to interface with - * an arbitrary number of [class@Gtk.CellRenderer]s and interact with the user - * for a given [iface@Gtk.TreeModel] row. - * - * The cell area handles events, focus navigation, drawing and - * size requests and allocations for a given row of data. - * - * Usually users dont have to interact with the `GtkCellArea` directly - * unless they are implementing a cell-layouting widget themselves. - * - * # Requesting area sizes - * - * As outlined in - * [GtkWidget’s geometry management section](class.Widget.html#height-for-width-geometry-management), - * GTK uses a height-for-width - * geometry management system to compute the sizes of widgets and user - * interfaces. `GtkCellArea` uses the same semantics to calculate the - * size of an area for an arbitrary number of `GtkTreeModel` rows. - * - * When requesting the size of a cell area one needs to calculate - * the size for a handful of rows, and this will be done differently by - * different layouting widgets. For instance a [class@Gtk.TreeViewColumn] - * always lines up the areas from top to bottom while a [class@Gtk.IconView] - * on the other hand might enforce that all areas received the same - * width and wrap the areas around, requesting height for more cell - * areas when allocated less width. - * - * It’s also important for areas to maintain some cell - * alignments with areas rendered for adjacent rows (cells can - * appear “columnized” inside an area even when the size of - * cells are different in each row). For this reason the `GtkCellArea` - * uses a [class@Gtk.CellAreaContext] object to store the alignments - * and sizes along the way (as well as the overall largest minimum - * and natural size for all the rows which have been calculated - * with the said context). - * - * The [class@Gtk.CellAreaContext] is an opaque object specific to the - * `GtkCellArea` which created it (see [method@Gtk.CellArea.create_context]). - * - * The owning cell-layouting widget can create as many contexts as - * it wishes to calculate sizes of rows which should receive the - * same size in at least one orientation (horizontally or vertically), - * However, it’s important that the same [class@Gtk.CellAreaContext] which - * was used to request the sizes for a given `GtkTreeModel` row be - * used when rendering or processing events for that row. - * - * In order to request the width of all the rows at the root level - * of a `GtkTreeModel` one would do the following: - * - * ```c - * GtkTreeIter iter; - * int minimum_width; - * int natural_width; - * - * valid = gtk_tree_model_get_iter_first (model, &iter); - * while (valid) - * { - * gtk_cell_area_apply_attributes (area, model, &iter, FALSE, FALSE); - * gtk_cell_area_get_preferred_width (area, context, widget, NULL, NULL); - * - * valid = gtk_tree_model_iter_next (model, &iter); - * } - * - * gtk_cell_area_context_get_preferred_width (context, &minimum_width, &natural_width); - * ``` - * - * Note that in this example it’s not important to observe the - * returned minimum and natural width of the area for each row - * unless the cell-layouting object is actually interested in the - * widths of individual rows. The overall width is however stored - * in the accompanying `GtkCellAreaContext` object and can be consulted - * at any time. - * - * This can be useful since `GtkCellLayout` widgets usually have to - * support requesting and rendering rows in treemodels with an - * exceedingly large amount of rows. The `GtkCellLayout` widget in - * that case would calculate the required width of the rows in an - * idle or timeout source (see [func@GLib.timeout_add]) and when the widget - * is requested its actual width in [vfunc@Gtk.Widget.measure] - * it can simply consult the width accumulated so far in the - * `GtkCellAreaContext` object. - * - * A simple example where rows are rendered from top to bottom and - * take up the full width of the layouting widget would look like: - * - * ```c - * static void - * foo_get_preferred_width (GtkWidget *widget, - * int *minimum_size, - * int *natural_size) - * { - * Foo *self = FOO (widget); - * FooPrivate *priv = foo_get_instance_private (self); - * - * foo_ensure_at_least_one_handfull_of_rows_have_been_requested (self); - * - * gtk_cell_area_context_get_preferred_width (priv->context, minimum_size, natural_size); - * } - * ``` - * - * In the above example the `Foo` widget has to make sure that some - * row sizes have been calculated (the amount of rows that `Foo` judged - * was appropriate to request space for in a single timeout iteration) - * before simply returning the amount of space required by the area via - * the `GtkCellAreaContext`. - * - * Requesting the height for width (or width for height) of an area is - * a similar task except in this case the `GtkCellAreaContext` does not - * store the data (actually, it does not know how much space the layouting - * widget plans to allocate it for every row. It’s up to the layouting - * widget to render each row of data with the appropriate height and - * width which was requested by the `GtkCellArea`). - * - * In order to request the height for width of all the rows at the - * root level of a `GtkTreeModel` one would do the following: - * - * ```c - * GtkTreeIter iter; - * int minimum_height; - * int natural_height; - * int full_minimum_height = 0; - * int full_natural_height = 0; - * - * valid = gtk_tree_model_get_iter_first (model, &iter); - * while (valid) - * { - * gtk_cell_area_apply_attributes (area, model, &iter, FALSE, FALSE); - * gtk_cell_area_get_preferred_height_for_width (area, context, widget, - * width, &minimum_height, &natural_height); - * - * if (width_is_for_allocation) - * cache_row_height (&iter, minimum_height, natural_height); - * - * full_minimum_height += minimum_height; - * full_natural_height += natural_height; - * - * valid = gtk_tree_model_iter_next (model, &iter); - * } - * ``` - * - * Note that in the above example we would need to cache the heights - * returned for each row so that we would know what sizes to render the - * areas for each row. However we would only want to really cache the - * heights if the request is intended for the layouting widgets real - * allocation. - * - * In some cases the layouting widget is requested the height for an - * arbitrary for_width, this is a special case for layouting widgets - * who need to request size for tens of thousands of rows. For this - * case it’s only important that the layouting widget calculate - * one reasonably sized chunk of rows and return that height - * synchronously. The reasoning here is that any layouting widget is - * at least capable of synchronously calculating enough height to fill - * the screen height (or scrolled window height) in response to a single - * call to [vfunc@Gtk.Widget.measure]. Returning - * a perfect height for width that is larger than the screen area is - * inconsequential since after the layouting receives an allocation - * from a scrolled window it simply continues to drive the scrollbar - * values while more and more height is required for the row heights - * that are calculated in the background. - * - * # Rendering Areas - * - * Once area sizes have been acquired at least for the rows in the - * visible area of the layouting widget they can be rendered at - * [vfunc@Gtk.Widget.snapshot] time. - * - * A crude example of how to render all the rows at the root level - * runs as follows: - * - * ```c - * GtkAllocation allocation; - * GdkRectangle cell_area = { 0, }; - * GtkTreeIter iter; - * int minimum_width; - * int natural_width; - * - * gtk_widget_get_allocation (widget, &allocation); - * cell_area.width = allocation.width; - * - * valid = gtk_tree_model_get_iter_first (model, &iter); - * while (valid) - * { - * cell_area.height = get_cached_height_for_row (&iter); - * - * gtk_cell_area_apply_attributes (area, model, &iter, FALSE, FALSE); - * gtk_cell_area_render (area, context, widget, cr, - * &cell_area, &cell_area, state_flags, FALSE); - * - * cell_area.y += cell_area.height; - * - * valid = gtk_tree_model_iter_next (model, &iter); - * } - * ``` - * - * Note that the cached height in this example really depends on how - * the layouting widget works. The layouting widget might decide to - * give every row its minimum or natural height or, if the model content - * is expected to fit inside the layouting widget without scrolling, it - * would make sense to calculate the allocation for each row at - * the time the widget is allocated using [func@Gtk.distribute_natural_allocation]. - * - * # Handling Events and Driving Keyboard Focus - * - * Passing events to the area is as simple as handling events on any - * normal widget and then passing them to the [method@Gtk.CellArea.event] - * API as they come in. Usually `GtkCellArea` is only interested in - * button events, however some customized derived areas can be implemented - * who are interested in handling other events. Handling an event can - * trigger the [`signal@Gtk.CellArea::focus-changed`] signal to fire; as well - * as [`signal@GtkCellArea::add-editable`] in the case that an editable cell - * was clicked and needs to start editing. You can call - * [method@Gtk.CellArea.stop_editing] at any time to cancel any cell editing - * that is currently in progress. - * - * The `GtkCellArea` drives keyboard focus from cell to cell in a way - * similar to `GtkWidget`. For layouting widgets that support giving - * focus to cells it’s important to remember to pass `GTK_CELL_RENDERER_FOCUSED` - * to the area functions for the row that has focus and to tell the - * area to paint the focus at render time. - * - * Layouting widgets that accept focus on cells should implement the - * [vfunc@Gtk.Widget.focus] virtual method. The layouting widget is always - * responsible for knowing where `GtkTreeModel` rows are rendered inside - * the widget, so at [vfunc@Gtk.Widget.focus] time the layouting widget - * should use the `GtkCellArea` methods to navigate focus inside the area - * and then observe the [enum@Gtk.DirectionType] to pass the focus to adjacent - * rows and areas. - * - * A basic example of how the [vfunc@Gtk.Widget.focus] virtual method - * should be implemented: - * - * ``` - * static gboolean - * foo_focus (GtkWidget *widget, - * GtkDirectionType direction) - * { - * Foo *self = FOO (widget); - * FooPrivate *priv = foo_get_instance_private (self); - * int focus_row = priv->focus_row; - * gboolean have_focus = FALSE; - * - * if (!gtk_widget_has_focus (widget)) - * gtk_widget_grab_focus (widget); - * - * valid = gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, priv->focus_row); - * while (valid) - * { - * gtk_cell_area_apply_attributes (priv->area, priv->model, &iter, FALSE, FALSE); - * - * if (gtk_cell_area_focus (priv->area, direction)) - * { - * priv->focus_row = focus_row; - * have_focus = TRUE; - * break; - * } - * else - * { - * if (direction == GTK_DIR_RIGHT || - * direction == GTK_DIR_LEFT) - * break; - * else if (direction == GTK_DIR_UP || - * direction == GTK_DIR_TAB_BACKWARD) - * { - * if (focus_row == 0) - * break; - * else - * { - * focus_row--; - * valid = gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, focus_row); - * } - * } - * else - * { - * if (focus_row == last_row) - * break; - * else - * { - * focus_row++; - * valid = gtk_tree_model_iter_next (priv->model, &iter); - * } - * } - * } - * } - * return have_focus; - * } - * ``` - * - * Note that the layouting widget is responsible for matching the - * `GtkDirectionType` values to the way it lays out its cells. - * - * # Cell Properties - * - * The `GtkCellArea` introduces cell properties for `GtkCellRenderer`s. - * This provides some general interfaces for defining the relationship - * cell areas have with their cells. For instance in a [class@Gtk.CellAreaBox] - * a cell might “expand” and receive extra space when the area is allocated - * more than its full natural request, or a cell might be configured to “align” - * with adjacent rows which were requested and rendered with the same - * `GtkCellAreaContext`. - * - * Use [method@Gtk.CellAreaClass.install_cell_property] to install cell - * properties for a cell area class and [method@Gtk.CellAreaClass.find_cell_property] - * or [method@Gtk.CellAreaClass.list_cell_properties] to get information about - * existing cell properties. - * - * To set the value of a cell property, use [method@Gtk.CellArea.cell_set_property], - * [method@Gtk.CellArea.cell_set] or [method@Gtk.CellArea.cell_set_valist]. To obtain - * the value of a cell property, use [method@Gtk.CellArea.cell_get_property] - * [method@Gtk.CellArea.cell_get] or [method@Gtk.CellArea.cell_get_valist]. - */ - -#include "config.h" - -#include -#include -#include - -#include "gtkcelllayout.h" -#include "gtkcellarea.h" -#include "gtkcellareacontext.h" -#include "gtkmarshalers.h" -#include "gtkprivate.h" -#include "gtksnapshot.h" -#include "gtkstylecontext.h" -#include "gtknative.h" - -#include - - -/* GObjectClass */ -static void gtk_cell_area_dispose (GObject *object); -static void gtk_cell_area_finalize (GObject *object); -static void gtk_cell_area_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec); -static void gtk_cell_area_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec); - -/* GtkCellAreaClass */ -static void gtk_cell_area_real_add (GtkCellArea *area, - GtkCellRenderer *renderer); -static void gtk_cell_area_real_remove (GtkCellArea *area, - GtkCellRenderer *renderer); -static void gtk_cell_area_real_foreach (GtkCellArea *area, - GtkCellCallback callback, - gpointer callback_data); -static void gtk_cell_area_real_foreach_alloc (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - const GdkRectangle *cell_area, - const GdkRectangle *background_area, - GtkCellAllocCallback callback, - gpointer callback_data); -static int gtk_cell_area_real_event (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - GdkEvent *event, - const GdkRectangle *cell_area, - GtkCellRendererState flags); -static void gtk_cell_area_real_snapshot (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - GtkSnapshot *snapshot, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - GtkCellRendererState flags, - gboolean paint_focus); -static void gtk_cell_area_real_apply_attributes (GtkCellArea *area, - GtkTreeModel *tree_model, - GtkTreeIter *iter, - gboolean is_expander, - gboolean is_expanded); - -static GtkCellAreaContext *gtk_cell_area_real_create_context (GtkCellArea *area); -static GtkCellAreaContext *gtk_cell_area_real_copy_context (GtkCellArea *area, - GtkCellAreaContext *context); -static GtkSizeRequestMode gtk_cell_area_real_get_request_mode (GtkCellArea *area); -static void gtk_cell_area_real_get_preferred_width (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - int *minimum_width, - int *natural_width); -static void gtk_cell_area_real_get_preferred_height (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - int *minimum_height, - int *natural_height); -static void gtk_cell_area_real_get_preferred_height_for_width (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - int width, - int *minimum_height, - int *natural_height); -static void gtk_cell_area_real_get_preferred_width_for_height (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - int height, - int *minimum_width, - int *natural_width); -static gboolean gtk_cell_area_real_is_activatable (GtkCellArea *area); -static gboolean gtk_cell_area_real_activate (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - const GdkRectangle *cell_area, - GtkCellRendererState flags, - gboolean edit_only); -static gboolean gtk_cell_area_real_focus (GtkCellArea *area, - GtkDirectionType direction); - -/* GtkCellLayoutIface */ -static void gtk_cell_area_cell_layout_init (GtkCellLayoutIface *iface); -static void gtk_cell_area_pack_default (GtkCellLayout *cell_layout, - GtkCellRenderer *renderer, - gboolean expand); -static void gtk_cell_area_clear (GtkCellLayout *cell_layout); -static void gtk_cell_area_add_attribute (GtkCellLayout *cell_layout, - GtkCellRenderer *renderer, - const char *attribute, - int column); -static void gtk_cell_area_set_cell_data_func (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - GtkCellLayoutDataFunc func, - gpointer func_data, - GDestroyNotify destroy); -static void gtk_cell_area_clear_attributes (GtkCellLayout *cell_layout, - GtkCellRenderer *renderer); -static void gtk_cell_area_reorder (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - int position); -static GList *gtk_cell_area_get_cells (GtkCellLayout *cell_layout); -static GtkCellArea *gtk_cell_area_get_area (GtkCellLayout *cell_layout); - -/* GtkBuildableIface */ -static void gtk_cell_area_buildable_init (GtkBuildableIface *iface); -static void gtk_cell_area_buildable_custom_tag_end (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const char *tagname, - gpointer data); - -/* Used in foreach loop to check if a child renderer is present */ -typedef struct { - GtkCellRenderer *renderer; - gboolean has_renderer; -} HasRendererCheck; - -/* Used in foreach loop to get a cell's allocation */ -typedef struct { - GtkCellRenderer *renderer; - GdkRectangle allocation; -} RendererAllocationData; - -/* Used in foreach loop to render cells */ -typedef struct { - GtkCellArea *area; - GtkWidget *widget; - GtkSnapshot *snapshot; - GdkRectangle focus_rect; - GtkCellRendererState render_flags; - guint paint_focus : 1; - guint focus_all : 1; - guint first_focus : 1; -} CellRenderData; - -/* Used in foreach loop to get a cell by position */ -typedef struct { - int x; - int y; - GtkCellRenderer *renderer; - GdkRectangle cell_area; -} CellByPositionData; - -/* Attribute/Cell metadata */ -typedef struct { - const char *attribute; - int column; -} CellAttribute; - -typedef struct { - GSList *attributes; - - GtkCellLayoutDataFunc func; - gpointer data; - GDestroyNotify destroy; - GtkCellLayout *proxy; -} CellInfo; - -static CellInfo *cell_info_new (GtkCellLayoutDataFunc func, - gpointer data, - GDestroyNotify destroy); -static void cell_info_free (CellInfo *info); -static CellAttribute *cell_attribute_new (GtkCellRenderer *renderer, - const char *attribute, - int column); -static void cell_attribute_free (CellAttribute *attribute); -static int cell_attribute_find (CellAttribute *cell_attribute, - const char *attribute); - -/* Internal functions/signal emissions */ -static void gtk_cell_area_add_editable (GtkCellArea *area, - GtkCellRenderer *renderer, - GtkCellEditable *editable, - const GdkRectangle *cell_area); -static void gtk_cell_area_remove_editable (GtkCellArea *area, - GtkCellRenderer *renderer, - GtkCellEditable *editable); -static void gtk_cell_area_set_edit_widget (GtkCellArea *area, - GtkCellEditable *editable); -static void gtk_cell_area_set_edited_cell (GtkCellArea *area, - GtkCellRenderer *renderer); - - -/* Struct to pass data along while looping over - * cell renderers to apply attributes - */ -typedef struct { - GtkCellArea *area; - GtkTreeModel *model; - GtkTreeIter *iter; - gboolean is_expander; - gboolean is_expanded; -} AttributeData; - -typedef struct _GtkCellAreaPrivate GtkCellAreaPrivate; - -struct _GtkCellAreaPrivate -{ - /* The GtkCellArea bookkeeps any connected - * attributes in this hash table. - */ - GHashTable *cell_info; - - /* Current path is saved as a side-effect - * of gtk_cell_area_apply_attributes() - */ - char *current_path; - - /* Current cell being edited and editable widget used */ - GtkCellEditable *edit_widget; - GtkCellRenderer *edited_cell; - - /* Signal connections to the editable widget */ - gulong remove_widget_id; - - /* Currently focused cell */ - GtkCellRenderer *focus_cell; - - /* Tracking which cells are focus siblings of focusable cells */ - GHashTable *focus_siblings; -}; - -enum { - PROP_0, - PROP_FOCUS_CELL, - PROP_EDITED_CELL, - PROP_EDIT_WIDGET -}; - -enum { - SIGNAL_APPLY_ATTRIBUTES, - SIGNAL_ADD_EDITABLE, - SIGNAL_REMOVE_EDITABLE, - SIGNAL_FOCUS_CHANGED, - LAST_SIGNAL -}; - -/* Keep the paramspec pool internal, no need to deliver notifications - * on cells. at least no perceived need for now - */ -static GParamSpecPool *cell_property_pool = NULL; -static guint cell_area_signals[LAST_SIGNAL] = { 0 }; - -#define PARAM_SPEC_PARAM_ID(pspec) ((pspec)->param_id) -#define PARAM_SPEC_SET_PARAM_ID(pspec, id) ((pspec)->param_id = (id)) - -G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GtkCellArea, gtk_cell_area, G_TYPE_INITIALLY_UNOWNED, - G_ADD_PRIVATE (GtkCellArea) - G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT, - gtk_cell_area_cell_layout_init) - G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, - gtk_cell_area_buildable_init)) - -static void -gtk_cell_area_init (GtkCellArea *area) -{ - GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); - - priv->cell_info = g_hash_table_new_full (g_direct_hash, - g_direct_equal, - NULL, - (GDestroyNotify)cell_info_free); - - priv->focus_siblings = g_hash_table_new_full (g_direct_hash, - g_direct_equal, - NULL, - (GDestroyNotify)g_list_free); - - priv->focus_cell = NULL; - priv->edited_cell = NULL; - priv->edit_widget = NULL; - - priv->remove_widget_id = 0; -} - -static void -gtk_cell_area_class_init (GtkCellAreaClass *class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (class); - - /* GObjectClass */ - object_class->dispose = gtk_cell_area_dispose; - object_class->finalize = gtk_cell_area_finalize; - object_class->get_property = gtk_cell_area_get_property; - object_class->set_property = gtk_cell_area_set_property; - - /* general */ - class->add = gtk_cell_area_real_add; - class->remove = gtk_cell_area_real_remove; - class->foreach = gtk_cell_area_real_foreach; - class->foreach_alloc = gtk_cell_area_real_foreach_alloc; - class->event = gtk_cell_area_real_event; - class->snapshot = gtk_cell_area_real_snapshot; - class->apply_attributes = gtk_cell_area_real_apply_attributes; - - /* geometry */ - class->create_context = gtk_cell_area_real_create_context; - class->copy_context = gtk_cell_area_real_copy_context; - class->get_request_mode = gtk_cell_area_real_get_request_mode; - class->get_preferred_width = gtk_cell_area_real_get_preferred_width; - class->get_preferred_height = gtk_cell_area_real_get_preferred_height; - class->get_preferred_height_for_width = gtk_cell_area_real_get_preferred_height_for_width; - class->get_preferred_width_for_height = gtk_cell_area_real_get_preferred_width_for_height; - - /* focus */ - class->is_activatable = gtk_cell_area_real_is_activatable; - class->activate = gtk_cell_area_real_activate; - class->focus = gtk_cell_area_real_focus; - - /* Signals */ - /** - * GtkCellArea::apply-attributes: - * @area: the `GtkCellArea` to apply the attributes to - * @model: the `GtkTreeModel` to apply the attributes from - * @iter: the `GtkTreeIter` indicating which row to apply the attributes of - * @is_expander: whether the view shows children for this row - * @is_expanded: whether the view is currently showing the children of this row - * - * This signal is emitted whenever applying attributes to @area from @model - */ - cell_area_signals[SIGNAL_APPLY_ATTRIBUTES] = - g_signal_new (I_("apply-attributes"), - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (GtkCellAreaClass, apply_attributes), - NULL, NULL, - _gtk_marshal_VOID__OBJECT_BOXED_BOOLEAN_BOOLEAN, - G_TYPE_NONE, 4, - GTK_TYPE_TREE_MODEL, - GTK_TYPE_TREE_ITER, - G_TYPE_BOOLEAN, - G_TYPE_BOOLEAN); - g_signal_set_va_marshaller (cell_area_signals[SIGNAL_APPLY_ATTRIBUTES], G_TYPE_FROM_CLASS (class), - _gtk_marshal_VOID__OBJECT_BOXED_BOOLEAN_BOOLEANv); - - /** - * GtkCellArea::add-editable: - * @area: the `GtkCellArea` where editing started - * @renderer: the `GtkCellRenderer` that started the edited - * @editable: the `GtkCellEditable` widget to add - * @cell_area: the `GtkWidget` relative `GdkRectangle` coordinates - * where @editable should be added - * @path: the `GtkTreePath` string this edit was initiated for - * - * Indicates that editing has started on @renderer and that @editable - * should be added to the owning cell-layouting widget at @cell_area. - */ - cell_area_signals[SIGNAL_ADD_EDITABLE] = - g_signal_new (I_("add-editable"), - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, /* No class closure here */ - NULL, NULL, - _gtk_marshal_VOID__OBJECT_OBJECT_BOXED_STRING, - G_TYPE_NONE, 4, - GTK_TYPE_CELL_RENDERER, - GTK_TYPE_CELL_EDITABLE, - GDK_TYPE_RECTANGLE, - G_TYPE_STRING); - - - /** - * GtkCellArea::remove-editable: - * @area: the `GtkCellArea` where editing finished - * @renderer: the `GtkCellRenderer` that finished editeding - * @editable: the `GtkCellEditable` widget to remove - * - * Indicates that editing finished on @renderer and that @editable - * should be removed from the owning cell-layouting widget. - */ - cell_area_signals[SIGNAL_REMOVE_EDITABLE] = - g_signal_new (I_("remove-editable"), - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, /* No class closure here */ - NULL, NULL, - _gtk_marshal_VOID__OBJECT_OBJECT, - G_TYPE_NONE, 2, - GTK_TYPE_CELL_RENDERER, - GTK_TYPE_CELL_EDITABLE); - - /** - * GtkCellArea::focus-changed: - * @area: the `GtkCellArea` where focus changed - * @renderer: the `GtkCellRenderer` that has focus - * @path: the current `GtkTreePath` string set for @area - * - * Indicates that focus changed on this @area. This signal - * is emitted either as a result of focus handling or event - * handling. - * - * It's possible that the signal is emitted even if the - * currently focused renderer did not change, this is - * because focus may change to the same renderer in the - * same cell area for a different row of data. - */ - cell_area_signals[SIGNAL_FOCUS_CHANGED] = - g_signal_new (I_("focus-changed"), - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, /* No class closure here */ - NULL, NULL, - _gtk_marshal_VOID__OBJECT_STRING, - G_TYPE_NONE, 2, - GTK_TYPE_CELL_RENDERER, - G_TYPE_STRING); - - /* Properties */ - /** - * GtkCellArea:focus-cell: - * - * The cell in the area that currently has focus - */ - g_object_class_install_property (object_class, - PROP_FOCUS_CELL, - g_param_spec_object ("focus-cell", NULL, NULL, - GTK_TYPE_CELL_RENDERER, - GTK_PARAM_READWRITE)); - - /** - * GtkCellArea:edited-cell: - * - * The cell in the area that is currently edited - * - * This property is read-only and only changes as - * a result of a call gtk_cell_area_activate_cell(). - */ - g_object_class_install_property (object_class, - PROP_EDITED_CELL, - g_param_spec_object ("edited-cell", NULL, NULL, - GTK_TYPE_CELL_RENDERER, - GTK_PARAM_READABLE)); - - /** - * GtkCellArea:edit-widget: - * - * The widget currently editing the edited cell - * - * This property is read-only and only changes as - * a result of a call gtk_cell_area_activate_cell(). - */ - g_object_class_install_property (object_class, - PROP_EDIT_WIDGET, - g_param_spec_object ("edit-widget", NULL, NULL, - GTK_TYPE_CELL_EDITABLE, - GTK_PARAM_READABLE)); - - /* Pool for Cell Properties */ - if (!cell_property_pool) - cell_property_pool = g_param_spec_pool_new (FALSE); -} - -/************************************************************* - * CellInfo Basics * - *************************************************************/ -static CellInfo * -cell_info_new (GtkCellLayoutDataFunc func, - gpointer data, - GDestroyNotify destroy) -{ - CellInfo *info = g_slice_new0 (CellInfo); - - info->func = func; - info->data = data; - info->destroy = destroy; - - return info; -} - -static void -cell_info_free (CellInfo *info) -{ - if (info->destroy) - info->destroy (info->data); - - g_slist_free_full (info->attributes, (GDestroyNotify)cell_attribute_free); - - g_slice_free (CellInfo, info); -} - -static CellAttribute * -cell_attribute_new (GtkCellRenderer *renderer, - const char *attribute, - int column) -{ - GParamSpec *pspec; - - /* Check if the attribute really exists and point to - * the property string installed on the cell renderer - * class (dont dup the string) - */ - pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (renderer), attribute); - - if (pspec) - { - CellAttribute *cell_attribute = g_slice_new (CellAttribute); - - cell_attribute->attribute = pspec->name; - cell_attribute->column = column; - - return cell_attribute; - } - - return NULL; -} - -static void -cell_attribute_free (CellAttribute *attribute) -{ - g_slice_free (CellAttribute, attribute); -} - -/* GCompareFunc for g_slist_find_custom() */ -static int -cell_attribute_find (CellAttribute *cell_attribute, - const char *attribute) -{ - return g_strcmp0 (cell_attribute->attribute, attribute); -} - -/************************************************************* - * GObjectClass * - *************************************************************/ -static void -gtk_cell_area_finalize (GObject *object) -{ - GtkCellArea *area = GTK_CELL_AREA (object); - GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); - - /* All cell renderers should already be removed at this point, - * just kill our (empty) hash tables here. - */ - g_hash_table_destroy (priv->cell_info); - g_hash_table_destroy (priv->focus_siblings); - - g_free (priv->current_path); - - G_OBJECT_CLASS (gtk_cell_area_parent_class)->finalize (object); -} - - -static void -gtk_cell_area_dispose (GObject *object) -{ - /* This removes every cell renderer that may be added to the GtkCellArea, - * subclasses should be breaking references to the GtkCellRenderers - * at this point. - */ - gtk_cell_layout_clear (GTK_CELL_LAYOUT (object)); - - /* Remove any ref to a focused/edited cell */ - gtk_cell_area_set_focus_cell (GTK_CELL_AREA (object), NULL); - gtk_cell_area_set_edited_cell (GTK_CELL_AREA (object), NULL); - gtk_cell_area_set_edit_widget (GTK_CELL_AREA (object), NULL); - - G_OBJECT_CLASS (gtk_cell_area_parent_class)->dispose (object); -} - -static void -gtk_cell_area_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - GtkCellArea *area = GTK_CELL_AREA (object); - - switch (prop_id) - { - case PROP_FOCUS_CELL: - gtk_cell_area_set_focus_cell (area, (GtkCellRenderer *)g_value_get_object (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gtk_cell_area_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GtkCellArea *area = GTK_CELL_AREA (object); - GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); - - switch (prop_id) - { - case PROP_FOCUS_CELL: - g_value_set_object (value, priv->focus_cell); - break; - case PROP_EDITED_CELL: - g_value_set_object (value, priv->edited_cell); - break; - case PROP_EDIT_WIDGET: - g_value_set_object (value, priv->edit_widget); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -/************************************************************* - * GtkCellAreaClass * - *************************************************************/ -static void -gtk_cell_area_real_add (GtkCellArea *area, - GtkCellRenderer *renderer) -{ - g_warning ("GtkCellAreaClass::add not implemented for '%s'", - g_type_name (G_TYPE_FROM_INSTANCE (area))); -} - -static void -gtk_cell_area_real_remove (GtkCellArea *area, - GtkCellRenderer *renderer) -{ - g_warning ("GtkCellAreaClass::remove not implemented for '%s'", - g_type_name (G_TYPE_FROM_INSTANCE (area))); -} - -static void -gtk_cell_area_real_foreach (GtkCellArea *area, - GtkCellCallback callback, - gpointer callback_data) -{ - g_warning ("GtkCellAreaClass::foreach not implemented for '%s'", - g_type_name (G_TYPE_FROM_INSTANCE (area))); -} - -static void -gtk_cell_area_real_foreach_alloc (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - const GdkRectangle *cell_area, - const GdkRectangle *background_area, - GtkCellAllocCallback callback, - gpointer callback_data) -{ - g_warning ("GtkCellAreaClass::foreach_alloc not implemented for '%s'", - g_type_name (G_TYPE_FROM_INSTANCE (area))); -} - -static int -gtk_cell_area_real_event (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - GdkEvent *event, - const GdkRectangle *cell_area, - GtkCellRendererState flags) -{ - GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); - gboolean retval = FALSE; - GdkEventType event_type = gdk_event_get_event_type (event); - - if (event_type == GDK_KEY_PRESS && (flags & GTK_CELL_RENDERER_FOCUSED) != 0) - { - /* Cancel any edits in progress */ - if (priv->edited_cell && - gdk_key_event_get_keyval (event) == GDK_KEY_Escape) - { - gtk_cell_area_stop_editing (area, TRUE); - retval = TRUE; - } - } - else if (event_type == GDK_BUTTON_PRESS) - { - guint button; - - button = gdk_button_event_get_button (event); - if (button == GDK_BUTTON_PRIMARY) - { - GtkCellRenderer *renderer = NULL; - GtkCellRenderer *focus_renderer; - GdkRectangle alloc_area; - double event_x, event_y; - double nx, ny; - double x, y; - GtkNative *native; - - /* We may need some semantics to tell us the offset of the event - * window we are handling events for (i.e. GtkTreeView has a bin_window) */ - gdk_event_get_position (event, &event_x, &event_y); - - native = gtk_widget_get_native (widget); - gtk_native_get_surface_transform (native, &nx, &ny); - gtk_widget_translate_coordinates (GTK_WIDGET (native), widget, event_x - nx, event_y - ny, &x, &y); - event_x = x; - event_y = y; - - /* Dont try to search for an event coordinate that is not in the area, that will - * trigger a runtime warning. - */ - if (event_x >= cell_area->x && event_x <= cell_area->x + cell_area->width && - event_y >= cell_area->y && event_y <= cell_area->y + cell_area->height) - renderer = - gtk_cell_area_get_cell_at_position (area, context, widget, - cell_area, event_x, event_y, - &alloc_area); - - if (renderer) - { - focus_renderer = gtk_cell_area_get_focus_from_sibling (area, renderer); - if (!focus_renderer) - focus_renderer = renderer; - - /* If we're already editing, cancel it and set focus */ - if (gtk_cell_area_get_edited_cell (area)) - { - /* XXX Was it really canceled in this case ? */ - gtk_cell_area_stop_editing (area, TRUE); - gtk_cell_area_set_focus_cell (area, focus_renderer); - retval = TRUE; - } - else - { - /* If we are activating via a focus sibling, - * we need to fetch the right cell area for the real event renderer */ - if (focus_renderer != renderer) - gtk_cell_area_get_cell_allocation (area, context, widget, focus_renderer, - cell_area, &alloc_area); - - gtk_cell_area_set_focus_cell (area, focus_renderer); - retval = gtk_cell_area_activate_cell (area, widget, focus_renderer, - event, &alloc_area, flags); - } - } - } - } - - return retval; -} - -static gboolean -snapshot_cell (GtkCellRenderer *renderer, - const GdkRectangle *cell_area, - const GdkRectangle *cell_background, - CellRenderData *data) -{ - GtkCellRenderer *focus_cell; - GtkCellRendererState flags; - GdkRectangle inner_area; - - focus_cell = gtk_cell_area_get_focus_cell (data->area); - flags = data->render_flags; - - gtk_cell_area_inner_cell_area (data->area, data->widget, cell_area, &inner_area); - - if ((flags & GTK_CELL_RENDERER_FOCUSED) && - (data->focus_all || - (focus_cell && - (renderer == focus_cell || - gtk_cell_area_is_focus_sibling (data->area, focus_cell, renderer))))) - { - GdkRectangle cell_focus; - - gtk_cell_renderer_get_aligned_area (renderer, data->widget, flags, &inner_area, &cell_focus); - - if (data->first_focus) - { - data->first_focus = FALSE; - data->focus_rect = cell_focus; - } - else - { - gdk_rectangle_union (&data->focus_rect, &cell_focus, &data->focus_rect); - } - } - - gtk_cell_renderer_snapshot (renderer, data->snapshot, data->widget, - cell_background, &inner_area, flags); - - return FALSE; -} - -static void -gtk_cell_area_real_snapshot (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - GtkSnapshot *snapshot, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - GtkCellRendererState flags, - gboolean paint_focus) -{ - CellRenderData render_data = - { - area, - widget, - snapshot, - { 0, }, - flags, - paint_focus, - FALSE, TRUE - }; - - /* Make sure we dont paint a focus rectangle while there - * is an editable widget in play - */ - if (gtk_cell_area_get_edited_cell (area)) - render_data.paint_focus = FALSE; - - if (!gtk_widget_has_visible_focus (widget)) - render_data.paint_focus = FALSE; - - /* If no cell can activate but the caller wants focus painted, - * then we paint focus around all cells */ - if ((flags & GTK_CELL_RENDERER_FOCUSED) != 0 && paint_focus && - !gtk_cell_area_is_activatable (area)) - render_data.focus_all = TRUE; - - gtk_cell_area_foreach_alloc (area, context, widget, cell_area, background_area, - (GtkCellAllocCallback) snapshot_cell, &render_data); - - if (render_data.paint_focus && - render_data.focus_rect.width != 0 && - render_data.focus_rect.height != 0) - { - GtkStyleContext *style_context; - GtkStateFlags renderer_state = 0; - - style_context = gtk_widget_get_style_context (widget); - gtk_style_context_save (style_context); - - renderer_state = gtk_cell_renderer_get_state (NULL, widget, flags); - gtk_style_context_set_state (style_context, renderer_state); - - gtk_snapshot_render_focus (snapshot, style_context, - render_data.focus_rect.x, render_data.focus_rect.y, - render_data.focus_rect.width, render_data.focus_rect.height); - - gtk_style_context_restore (style_context); - } -} - -static void -apply_cell_attributes (GtkCellRenderer *renderer, - CellInfo *info, - AttributeData *data) -{ - CellAttribute *attribute; - GSList *list; - GValue value = G_VALUE_INIT; - gboolean is_expander; - gboolean is_expanded; - - g_object_freeze_notify (G_OBJECT (renderer)); - - /* Whether a row expands or is presently expanded can only be - * provided by the view (as these states can vary across views - * accessing the same model). - */ - is_expander = gtk_cell_renderer_get_is_expander (renderer); - if (is_expander != data->is_expander) - gtk_cell_renderer_set_is_expander (renderer, data->is_expander); - - is_expanded = gtk_cell_renderer_get_is_expanded (renderer); - if (is_expanded != data->is_expanded) - gtk_cell_renderer_set_is_expanded (renderer, data->is_expanded); - - /* Apply the attributes directly to the renderer */ - for (list = info->attributes; list; list = list->next) - { - attribute = list->data; - - gtk_tree_model_get_value (data->model, data->iter, attribute->column, &value); - g_object_set_property (G_OBJECT (renderer), attribute->attribute, &value); - g_value_unset (&value); - } - - /* Call any GtkCellLayoutDataFunc that may have been set by the user - */ - if (info->func) - info->func (info->proxy ? info->proxy : GTK_CELL_LAYOUT (data->area), renderer, - data->model, data->iter, info->data); - - g_object_thaw_notify (G_OBJECT (renderer)); -} - -static void -gtk_cell_area_real_apply_attributes (GtkCellArea *area, - GtkTreeModel *tree_model, - GtkTreeIter *iter, - gboolean is_expander, - gboolean is_expanded) -{ - GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); - AttributeData data; - GtkTreePath *path; - - /* Feed in data needed to apply to every renderer */ - data.area = area; - data.model = tree_model; - data.iter = iter; - data.is_expander = is_expander; - data.is_expanded = is_expanded; - - /* Go over any cells that have attributes or custom GtkCellLayoutDataFuncs and - * apply the data from the treemodel */ - g_hash_table_foreach (priv->cell_info, (GHFunc)apply_cell_attributes, &data); - - /* Update the currently applied path */ - g_free (priv->current_path); - path = gtk_tree_model_get_path (tree_model, iter); - priv->current_path = gtk_tree_path_to_string (path); - gtk_tree_path_free (path); -} - -static GtkCellAreaContext * -gtk_cell_area_real_create_context (GtkCellArea *area) -{ - g_warning ("GtkCellAreaClass::create_context not implemented for '%s'", - g_type_name (G_TYPE_FROM_INSTANCE (area))); - - return NULL; -} - -static GtkCellAreaContext * -gtk_cell_area_real_copy_context (GtkCellArea *area, - GtkCellAreaContext *context) -{ - g_warning ("GtkCellAreaClass::copy_context not implemented for '%s'", - g_type_name (G_TYPE_FROM_INSTANCE (area))); - - return NULL; -} - -static GtkSizeRequestMode -gtk_cell_area_real_get_request_mode (GtkCellArea *area) -{ - /* By default cell areas are height-for-width. */ - return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH; -} - -static void -gtk_cell_area_real_get_preferred_width (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - int *minimum_width, - int *natural_width) -{ - g_warning ("GtkCellAreaClass::get_preferred_width not implemented for '%s'", - g_type_name (G_TYPE_FROM_INSTANCE (area))); -} - -static void -gtk_cell_area_real_get_preferred_height (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - int *minimum_height, - int *natural_height) -{ - g_warning ("GtkCellAreaClass::get_preferred_height not implemented for '%s'", - g_type_name (G_TYPE_FROM_INSTANCE (area))); -} - -static void -gtk_cell_area_real_get_preferred_height_for_width (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - int width, - int *minimum_height, - int *natural_height) -{ - /* If the area doesn't do height-for-width, fallback on base preferred height */ - GTK_CELL_AREA_GET_CLASS (area)->get_preferred_height (area, context, widget, minimum_height, natural_height); -} - -static void -gtk_cell_area_real_get_preferred_width_for_height (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - int height, - int *minimum_width, - int *natural_width) -{ - /* If the area doesn't do width-for-height, fallback on base preferred width */ - GTK_CELL_AREA_GET_CLASS (area)->get_preferred_width (area, context, widget, minimum_width, natural_width); -} - -static gboolean -get_is_activatable (GtkCellRenderer *renderer, - gboolean *activatable) -{ - - if (gtk_cell_renderer_is_activatable (renderer)) - *activatable = TRUE; - - return *activatable; -} - -static gboolean -gtk_cell_area_real_is_activatable (GtkCellArea *area) -{ - gboolean activatable = FALSE; - - /* Checks if any renderer can focus for the currently applied - * attributes. - * - * Subclasses can override this in the case that they are also - * rendering widgets as well as renderers. - */ - gtk_cell_area_foreach (area, (GtkCellCallback)get_is_activatable, &activatable); - - return activatable; -} - -static gboolean -gtk_cell_area_real_activate (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - const GdkRectangle *cell_area, - GtkCellRendererState flags, - gboolean edit_only) -{ - GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); - GdkRectangle renderer_area; - GtkCellRenderer *activate_cell = NULL; - GtkCellRendererMode mode; - - if (priv->focus_cell) - { - g_object_get (priv->focus_cell, "mode", &mode, NULL); - - if (gtk_cell_renderer_get_visible (priv->focus_cell) && - (edit_only ? mode == GTK_CELL_RENDERER_MODE_EDITABLE : - mode != GTK_CELL_RENDERER_MODE_INERT)) - activate_cell = priv->focus_cell; - } - else - { - GList *cells, *l; - - /* GtkTreeView sometimes wants to activate a cell when no - * cells are in focus. - */ - cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (area)); - for (l = cells; l && !activate_cell; l = l->next) - { - GtkCellRenderer *renderer = l->data; - - g_object_get (renderer, "mode", &mode, NULL); - - if (gtk_cell_renderer_get_visible (renderer) && - (edit_only ? mode == GTK_CELL_RENDERER_MODE_EDITABLE : - mode != GTK_CELL_RENDERER_MODE_INERT)) - activate_cell = renderer; - } - g_list_free (cells); - } - - if (activate_cell) - { - /* Get the allocation of the focused cell. - */ - gtk_cell_area_get_cell_allocation (area, context, widget, activate_cell, - cell_area, &renderer_area); - - /* Activate or Edit the cell - * - * Currently just not sending an event, renderers afaics dont use - * the event argument anyway, worst case is we can synthesize one. - */ - if (gtk_cell_area_activate_cell (area, widget, activate_cell, NULL, - &renderer_area, flags)) - return TRUE; - } - - return FALSE; -} - -static gboolean -gtk_cell_area_real_focus (GtkCellArea *area, - GtkDirectionType direction) -{ - g_warning ("GtkCellAreaClass::focus not implemented for '%s'", - g_type_name (G_TYPE_FROM_INSTANCE (area))); - return FALSE; -} - -/************************************************************* - * GtkCellLayoutIface * - *************************************************************/ -static void -gtk_cell_area_cell_layout_init (GtkCellLayoutIface *iface) -{ - iface->pack_start = gtk_cell_area_pack_default; - iface->pack_end = gtk_cell_area_pack_default; - iface->clear = gtk_cell_area_clear; - iface->add_attribute = gtk_cell_area_add_attribute; - iface->set_cell_data_func = gtk_cell_area_set_cell_data_func; - iface->clear_attributes = gtk_cell_area_clear_attributes; - iface->reorder = gtk_cell_area_reorder; - iface->get_cells = gtk_cell_area_get_cells; - iface->get_area = gtk_cell_area_get_area; -} - -static void -gtk_cell_area_pack_default (GtkCellLayout *cell_layout, - GtkCellRenderer *renderer, - gboolean expand) -{ - gtk_cell_area_add (GTK_CELL_AREA (cell_layout), renderer); -} - -static void -gtk_cell_area_clear (GtkCellLayout *cell_layout) -{ - GtkCellArea *area = GTK_CELL_AREA (cell_layout); - GList *l, *cells = - gtk_cell_layout_get_cells (cell_layout); - - for (l = cells; l; l = l->next) - { - GtkCellRenderer *renderer = l->data; - gtk_cell_area_remove (area, renderer); - } - - g_list_free (cells); -} - -static void -gtk_cell_area_add_attribute (GtkCellLayout *cell_layout, - GtkCellRenderer *renderer, - const char *attribute, - int column) -{ - gtk_cell_area_attribute_connect (GTK_CELL_AREA (cell_layout), - renderer, attribute, column); -} - -static void -gtk_cell_area_set_cell_data_func (GtkCellLayout *cell_layout, - GtkCellRenderer *renderer, - GtkCellLayoutDataFunc func, - gpointer func_data, - GDestroyNotify destroy) -{ - GtkCellArea *area = GTK_CELL_AREA (cell_layout); - - _gtk_cell_area_set_cell_data_func_with_proxy (area, renderer, (GFunc)func, func_data, destroy, NULL); -} - -static void -gtk_cell_area_clear_attributes (GtkCellLayout *cell_layout, - GtkCellRenderer *renderer) -{ - GtkCellArea *area = GTK_CELL_AREA (cell_layout); - GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); - CellInfo *info; - - info = g_hash_table_lookup (priv->cell_info, renderer); - - if (info) - { - g_slist_free_full (info->attributes, (GDestroyNotify)cell_attribute_free); - info->attributes = NULL; - } -} - -static void -gtk_cell_area_reorder (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - int position) -{ - g_warning ("GtkCellLayout::reorder not implemented for '%s'", - g_type_name (G_TYPE_FROM_INSTANCE (cell_layout))); -} - -static gboolean -accum_cells (GtkCellRenderer *renderer, - GList **accum) -{ - *accum = g_list_prepend (*accum, renderer); - - return FALSE; -} - -static GList * -gtk_cell_area_get_cells (GtkCellLayout *cell_layout) -{ - GList *cells = NULL; - - gtk_cell_area_foreach (GTK_CELL_AREA (cell_layout), - (GtkCellCallback)accum_cells, - &cells); - - return g_list_reverse (cells); -} - -static GtkCellArea * -gtk_cell_area_get_area (GtkCellLayout *cell_layout) -{ - return GTK_CELL_AREA (cell_layout); -} - -/************************************************************* - * GtkBuildableIface * - *************************************************************/ -static void -gtk_cell_area_buildable_init (GtkBuildableIface *iface) -{ - iface->add_child = _gtk_cell_layout_buildable_add_child; - iface->custom_tag_start = _gtk_cell_layout_buildable_custom_tag_start; - iface->custom_tag_end = gtk_cell_area_buildable_custom_tag_end; -} - -static void -gtk_cell_area_buildable_custom_tag_end (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const char *tagname, - gpointer data) -{ - /* Just ignore the boolean return from here */ - _gtk_cell_layout_buildable_custom_tag_end (buildable, builder, child, tagname, data); -} - -/************************************************************* - * API * - *************************************************************/ - -/** - * gtk_cell_area_add: - * @area: a `GtkCellArea` - * @renderer: the `GtkCellRenderer` to add to @area - * - * Adds @renderer to @area with the default child cell properties. - */ -void -gtk_cell_area_add (GtkCellArea *area, - GtkCellRenderer *renderer) -{ - g_return_if_fail (GTK_IS_CELL_AREA (area)); - g_return_if_fail (GTK_IS_CELL_RENDERER (renderer)); - - GTK_CELL_AREA_GET_CLASS (area)->add (area, renderer); -} - -/** - * gtk_cell_area_remove: - * @area: a `GtkCellArea` - * @renderer: the `GtkCellRenderer` to remove from @area - * - * Removes @renderer from @area. - */ -void -gtk_cell_area_remove (GtkCellArea *area, - GtkCellRenderer *renderer) -{ - GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); - GList *renderers, *l; - - g_return_if_fail (GTK_IS_CELL_AREA (area)); - g_return_if_fail (GTK_IS_CELL_RENDERER (renderer)); - - /* Remove any custom attributes and custom cell data func here first */ - g_hash_table_remove (priv->cell_info, renderer); - - /* Remove focus siblings of this renderer */ - g_hash_table_remove (priv->focus_siblings, renderer); - - /* Remove this renderer from any focus renderer's sibling list */ - renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (area)); - - for (l = renderers; l; l = l->next) - { - GtkCellRenderer *focus_renderer = l->data; - - if (gtk_cell_area_is_focus_sibling (area, focus_renderer, renderer)) - { - gtk_cell_area_remove_focus_sibling (area, focus_renderer, renderer); - break; - } - } - - g_list_free (renderers); - - GTK_CELL_AREA_GET_CLASS (area)->remove (area, renderer); -} - -static gboolean -get_has_renderer (GtkCellRenderer *renderer, - HasRendererCheck *check) -{ - if (renderer == check->renderer) - check->has_renderer = TRUE; - - return check->has_renderer; -} - -/** - * gtk_cell_area_has_renderer: - * @area: a `GtkCellArea` - * @renderer: the `GtkCellRenderer` to check - * - * Checks if @area contains @renderer. - * - * Returns: %TRUE if @renderer is in the @area. - */ -gboolean -gtk_cell_area_has_renderer (GtkCellArea *area, - GtkCellRenderer *renderer) -{ - HasRendererCheck check = { renderer, FALSE }; - - g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE); - g_return_val_if_fail (GTK_IS_CELL_RENDERER (renderer), FALSE); - - gtk_cell_area_foreach (area, (GtkCellCallback)get_has_renderer, &check); - - return check.has_renderer; -} - -/** - * gtk_cell_area_foreach: - * @area: a `GtkCellArea` - * @callback: (scope call): the `GtkCellCallback` to call - * @callback_data: user provided data pointer - * - * Calls @callback for every `GtkCellRenderer` in @area. - */ -void -gtk_cell_area_foreach (GtkCellArea *area, - GtkCellCallback callback, - gpointer callback_data) -{ - g_return_if_fail (GTK_IS_CELL_AREA (area)); - g_return_if_fail (callback != NULL); - - GTK_CELL_AREA_GET_CLASS (area)->foreach (area, callback, callback_data); -} - -/** - * gtk_cell_area_foreach_alloc: - * @area: a `GtkCellArea` - * @context: the `GtkCellArea`Context for this row of data. - * @widget: the `GtkWidget` that @area is rendering to - * @cell_area: the @widget relative coordinates and size for @area - * @background_area: the @widget relative coordinates of the background area - * @callback: (scope call): the `GtkCellAllocCallback` to call - * @callback_data: user provided data pointer - * - * Calls @callback for every `GtkCellRenderer` in @area with the - * allocated rectangle inside @cell_area. - */ -void -gtk_cell_area_foreach_alloc (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - const GdkRectangle *cell_area, - const GdkRectangle *background_area, - GtkCellAllocCallback callback, - gpointer callback_data) -{ - g_return_if_fail (GTK_IS_CELL_AREA (area)); - g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context)); - g_return_if_fail (GTK_IS_WIDGET (widget)); - g_return_if_fail (cell_area != NULL); - g_return_if_fail (callback != NULL); - - GTK_CELL_AREA_GET_CLASS (area)->foreach_alloc (area, context, widget, - cell_area, background_area, - callback, callback_data); -} - -/** - * gtk_cell_area_event: - * @area: a `GtkCellArea` - * @context: the `GtkCellArea`Context for this row of data. - * @widget: the `GtkWidget` that @area is rendering to - * @event: the `GdkEvent` to handle - * @cell_area: the @widget relative coordinates for @area - * @flags: the `GtkCellRenderer`State for @area in this row. - * - * Delegates event handling to a `GtkCellArea`. - * - * Returns: %TRUE if the event was handled by @area. - */ -int -gtk_cell_area_event (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - GdkEvent *event, - const GdkRectangle *cell_area, - GtkCellRendererState flags) -{ - GtkCellAreaClass *class; - - g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0); - g_return_val_if_fail (GTK_IS_CELL_AREA_CONTEXT (context), 0); - g_return_val_if_fail (GTK_IS_WIDGET (widget), 0); - g_return_val_if_fail (event != NULL, 0); - g_return_val_if_fail (cell_area != NULL, 0); - - class = GTK_CELL_AREA_GET_CLASS (area); - - if (class->event) - return class->event (area, context, widget, event, cell_area, flags); - - g_warning ("GtkCellAreaClass::event not implemented for '%s'", - g_type_name (G_TYPE_FROM_INSTANCE (area))); - return 0; -} - -/** - * gtk_cell_area_snapshot: - * @area: a `GtkCellArea` - * @context: the `GtkCellArea`Context for this row of data. - * @widget: the `GtkWidget` that @area is rendering to - * @snapshot: the `GtkSnapshot` to draw to - * @background_area: the @widget relative coordinates for @area’s background - * @cell_area: the @widget relative coordinates for @area - * @flags: the `GtkCellRenderer`State for @area in this row. - * @paint_focus: whether @area should paint focus on focused cells for focused rows or not. - * - * Snapshots @area’s cells according to @area’s layout onto at - * the given coordinates. - */ -void -gtk_cell_area_snapshot (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - GtkSnapshot *snapshot, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - GtkCellRendererState flags, - gboolean paint_focus) -{ - GtkCellAreaClass *class; - - g_return_if_fail (GTK_IS_CELL_AREA (area)); - g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context)); - g_return_if_fail (GTK_IS_WIDGET (widget)); - g_return_if_fail (snapshot != NULL); - g_return_if_fail (background_area != NULL); - g_return_if_fail (cell_area != NULL); - - class = GTK_CELL_AREA_GET_CLASS (area); - - if (class->snapshot) - class->snapshot (area, context, widget, snapshot, background_area, cell_area, flags, paint_focus); - else - g_warning ("GtkCellAreaClass::snapshot not implemented for '%s'", - g_type_name (G_TYPE_FROM_INSTANCE (area))); -} - -static gboolean -get_cell_allocation (GtkCellRenderer *renderer, - const GdkRectangle *cell_area, - const GdkRectangle *cell_background, - RendererAllocationData *data) -{ - if (data->renderer == renderer) - data->allocation = *cell_area; - - return (data->renderer == renderer); -} - -/** - * gtk_cell_area_get_cell_allocation: - * @area: a `GtkCellArea` - * @context: the `GtkCellArea`Context used to hold sizes for @area. - * @widget: the `GtkWidget` that @area is rendering on - * @renderer: the `GtkCellRenderer` to get the allocation for - * @cell_area: the whole allocated area for @area in @widget - * for this row - * @allocation: (out): where to store the allocation for @renderer - * - * Derives the allocation of @renderer inside @area if @area - * were to be renderered in @cell_area. - */ -void -gtk_cell_area_get_cell_allocation (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - GtkCellRenderer *renderer, - const GdkRectangle *cell_area, - GdkRectangle *allocation) -{ - RendererAllocationData data = { renderer, { 0, } }; - - g_return_if_fail (GTK_IS_CELL_AREA (area)); - g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context)); - g_return_if_fail (GTK_IS_WIDGET (widget)); - g_return_if_fail (GTK_IS_CELL_RENDERER (renderer)); - g_return_if_fail (cell_area != NULL); - g_return_if_fail (allocation != NULL); - - gtk_cell_area_foreach_alloc (area, context, widget, cell_area, cell_area, - (GtkCellAllocCallback)get_cell_allocation, &data); - - *allocation = data.allocation; -} - -static gboolean -get_cell_by_position (GtkCellRenderer *renderer, - const GdkRectangle *cell_area, - const GdkRectangle *cell_background, - CellByPositionData *data) -{ - if (data->x >= cell_area->x && data->x < cell_area->x + cell_area->width && - data->y >= cell_area->y && data->y < cell_area->y + cell_area->height) - { - data->renderer = renderer; - data->cell_area = *cell_area; - } - - return (data->renderer != NULL); -} - -/** - * gtk_cell_area_get_cell_at_position: - * @area: a `GtkCellArea` - * @context: the `GtkCellArea`Context used to hold sizes for @area. - * @widget: the `GtkWidget` that @area is rendering on - * @cell_area: the whole allocated area for @area in @widget - * for this row - * @x: the x position - * @y: the y position - * @alloc_area: (out) (optional): where to store the inner allocated area of the - * returned cell renderer - * - * Gets the `GtkCellRenderer` at @x and @y coordinates inside @area and optionally - * returns the full cell allocation for it inside @cell_area. - * - * Returns: (transfer none): the `GtkCellRenderer` at @x and @y. - */ -GtkCellRenderer * -gtk_cell_area_get_cell_at_position (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - const GdkRectangle *cell_area, - int x, - int y, - GdkRectangle *alloc_area) -{ - CellByPositionData data = { x, y, NULL, { 0, } }; - - g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL); - g_return_val_if_fail (GTK_IS_CELL_AREA_CONTEXT (context), NULL); - g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL); - g_return_val_if_fail (cell_area != NULL, NULL); - g_return_val_if_fail (x >= cell_area->x && x <= cell_area->x + cell_area->width, NULL); - g_return_val_if_fail (y >= cell_area->y && y <= cell_area->y + cell_area->height, NULL); - - gtk_cell_area_foreach_alloc (area, context, widget, cell_area, cell_area, - (GtkCellAllocCallback)get_cell_by_position, &data); - - if (alloc_area) - *alloc_area = data.cell_area; - - return data.renderer; -} - -/************************************************************* - * API: Geometry * - *************************************************************/ -/** - * gtk_cell_area_create_context: - * @area: a `GtkCellArea` - * - * Creates a `GtkCellArea`Context to be used with @area for - * all purposes. `GtkCellArea`Context stores geometry information - * for rows for which it was operated on, it is important to use - * the same context for the same row of data at all times (i.e. - * one should render and handle events with the same `GtkCellArea`Context - * which was used to request the size of those rows of data). - * - * Returns: (transfer full): a newly created `GtkCellArea`Context which can be used with @area. - */ -GtkCellAreaContext * -gtk_cell_area_create_context (GtkCellArea *area) -{ - g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL); - - return GTK_CELL_AREA_GET_CLASS (area)->create_context (area); -} - -/** - * gtk_cell_area_copy_context: - * @area: a `GtkCellArea` - * @context: the `GtkCellArea`Context to copy - * - * This is sometimes needed for cases where rows need to share - * alignments in one orientation but may be separately grouped - * in the opposing orientation. - * - * For instance, `GtkIconView` creates all icons (rows) to have - * the same width and the cells theirin to have the same - * horizontal alignments. However each row of icons may have - * a separate collective height. `GtkIconView` uses this to - * request the heights of each row based on a context which - * was already used to request all the row widths that are - * to be displayed. - * - * Returns: (transfer full): a newly created `GtkCellArea`Context copy of @context. - */ -GtkCellAreaContext * -gtk_cell_area_copy_context (GtkCellArea *area, - GtkCellAreaContext *context) -{ - g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL); - g_return_val_if_fail (GTK_IS_CELL_AREA_CONTEXT (context), NULL); - - return GTK_CELL_AREA_GET_CLASS (area)->copy_context (area, context); -} - -/** - * gtk_cell_area_get_request_mode: - * @area: a `GtkCellArea` - * - * Gets whether the area prefers a height-for-width layout - * or a width-for-height layout. - * - * Returns: The `GtkSizeRequestMode` preferred by @area. - */ -GtkSizeRequestMode -gtk_cell_area_get_request_mode (GtkCellArea *area) -{ - g_return_val_if_fail (GTK_IS_CELL_AREA (area), - GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH); - - return GTK_CELL_AREA_GET_CLASS (area)->get_request_mode (area); -} - -/** - * gtk_cell_area_get_preferred_width: - * @area: a `GtkCellArea` - * @context: the `GtkCellArea`Context to perform this request with - * @widget: the `GtkWidget` where @area will be rendering - * @minimum_width: (out) (optional): location to store the minimum width - * @natural_width: (out) (optional): location to store the natural width - * - * Retrieves a cell area’s initial minimum and natural width. - * - * @area will store some geometrical information in @context along the way; - * when requesting sizes over an arbitrary number of rows, it’s not important - * to check the @minimum_width and @natural_width of this call but rather to - * consult gtk_cell_area_context_get_preferred_width() after a series of - * requests. - */ -void -gtk_cell_area_get_preferred_width (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - int *minimum_width, - int *natural_width) -{ - g_return_if_fail (GTK_IS_CELL_AREA (area)); - g_return_if_fail (GTK_IS_WIDGET (widget)); - - GTK_CELL_AREA_GET_CLASS (area)->get_preferred_width (area, context, widget, - minimum_width, natural_width); -} - -/** - * gtk_cell_area_get_preferred_height_for_width: - * @area: a `GtkCellArea` - * @context: the `GtkCellArea`Context which has already been requested for widths. - * @widget: the `GtkWidget` where @area will be rendering - * @width: the width for which to check the height of this area - * @minimum_height: (out) (optional): location to store the minimum height - * @natural_height: (out) (optional): location to store the natural height - * - * Retrieves a cell area’s minimum and natural height if it would be given - * the specified @width. - * - * @area stores some geometrical information in @context along the way - * while calling gtk_cell_area_get_preferred_width(). It’s important to - * perform a series of gtk_cell_area_get_preferred_width() requests with - * @context first and then call gtk_cell_area_get_preferred_height_for_width() - * on each cell area individually to get the height for width of each - * fully requested row. - * - * If at some point, the width of a single row changes, it should be - * requested with gtk_cell_area_get_preferred_width() again and then - * the full width of the requested rows checked again with - * gtk_cell_area_context_get_preferred_width(). - */ -void -gtk_cell_area_get_preferred_height_for_width (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - int width, - int *minimum_height, - int *natural_height) -{ - GtkCellAreaClass *class; - - g_return_if_fail (GTK_IS_CELL_AREA (area)); - g_return_if_fail (GTK_IS_WIDGET (widget)); - - class = GTK_CELL_AREA_GET_CLASS (area); - class->get_preferred_height_for_width (area, context, widget, width, minimum_height, natural_height); -} - - -/** - * gtk_cell_area_get_preferred_height: - * @area: a `GtkCellArea` - * @context: the `GtkCellArea`Context to perform this request with - * @widget: the `GtkWidget` where @area will be rendering - * @minimum_height: (out) (optional): location to store the minimum height - * @natural_height: (out) (optional): location to store the natural height - * - * Retrieves a cell area’s initial minimum and natural height. - * - * @area will store some geometrical information in @context along the way; - * when requesting sizes over an arbitrary number of rows, it’s not important - * to check the @minimum_height and @natural_height of this call but rather to - * consult gtk_cell_area_context_get_preferred_height() after a series of - * requests. - */ -void -gtk_cell_area_get_preferred_height (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - int *minimum_height, - int *natural_height) -{ - g_return_if_fail (GTK_IS_CELL_AREA (area)); - g_return_if_fail (GTK_IS_WIDGET (widget)); - - GTK_CELL_AREA_GET_CLASS (area)->get_preferred_height (area, context, widget, - minimum_height, natural_height); -} - -/** - * gtk_cell_area_get_preferred_width_for_height: - * @area: a `GtkCellArea` - * @context: the `GtkCellArea`Context which has already been requested for widths. - * @widget: the `GtkWidget` where @area will be rendering - * @height: the height for which to check the width of this area - * @minimum_width: (out) (optional): location to store the minimum width - * @natural_width: (out) (optional): location to store the natural width - * - * Retrieves a cell area’s minimum and natural width if it would be given - * the specified @height. - * - * @area stores some geometrical information in @context along the way - * while calling gtk_cell_area_get_preferred_height(). It’s important to - * perform a series of gtk_cell_area_get_preferred_height() requests with - * @context first and then call gtk_cell_area_get_preferred_width_for_height() - * on each cell area individually to get the height for width of each - * fully requested row. - * - * If at some point, the height of a single row changes, it should be - * requested with gtk_cell_area_get_preferred_height() again and then - * the full height of the requested rows checked again with - * gtk_cell_area_context_get_preferred_height(). - */ -void -gtk_cell_area_get_preferred_width_for_height (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - int height, - int *minimum_width, - int *natural_width) -{ - GtkCellAreaClass *class; - - g_return_if_fail (GTK_IS_CELL_AREA (area)); - g_return_if_fail (GTK_IS_WIDGET (widget)); - - class = GTK_CELL_AREA_GET_CLASS (area); - class->get_preferred_width_for_height (area, context, widget, height, minimum_width, natural_width); -} - -/************************************************************* - * API: Attributes * - *************************************************************/ - -/** - * gtk_cell_area_attribute_connect: - * @area: a `GtkCellArea` - * @renderer: the `GtkCellRenderer` to connect an attribute for - * @attribute: the attribute name - * @column: the `GtkTreeModel` column to fetch attribute values from - * - * Connects an @attribute to apply values from @column for the - * `GtkTreeModel` in use. - */ -void -gtk_cell_area_attribute_connect (GtkCellArea *area, - GtkCellRenderer *renderer, - const char *attribute, - int column) -{ - GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); - CellInfo *info; - CellAttribute *cell_attribute; - - g_return_if_fail (GTK_IS_CELL_AREA (area)); - g_return_if_fail (GTK_IS_CELL_RENDERER (renderer)); - g_return_if_fail (attribute != NULL); - g_return_if_fail (gtk_cell_area_has_renderer (area, renderer)); - - info = g_hash_table_lookup (priv->cell_info, renderer); - - if (!info) - { - info = cell_info_new (NULL, NULL, NULL); - - g_hash_table_insert (priv->cell_info, renderer, info); - } - else - { - GSList *node; - - /* Check we are not adding the same attribute twice */ - if ((node = g_slist_find_custom (info->attributes, attribute, - (GCompareFunc)cell_attribute_find)) != NULL) - { - cell_attribute = node->data; - - g_warning ("Cannot connect attribute '%s' for cell renderer class '%s' " - "since '%s' is already attributed to column %d", - attribute, - G_OBJECT_TYPE_NAME (renderer), - attribute, cell_attribute->column); - return; - } - } - - cell_attribute = cell_attribute_new (renderer, attribute, column); - - if (!cell_attribute) - { - g_warning ("Cannot connect attribute '%s' for cell renderer class '%s' " - "since attribute does not exist", - attribute, - G_OBJECT_TYPE_NAME (renderer)); - return; - } - - info->attributes = g_slist_prepend (info->attributes, cell_attribute); -} - -/** - * gtk_cell_area_attribute_disconnect: - * @area: a `GtkCellArea` - * @renderer: the `GtkCellRenderer` to disconnect an attribute for - * @attribute: the attribute name - * - * Disconnects @attribute for the @renderer in @area so that - * attribute will no longer be updated with values from the - * model. - */ -void -gtk_cell_area_attribute_disconnect (GtkCellArea *area, - GtkCellRenderer *renderer, - const char *attribute) -{ - GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); - CellInfo *info; - CellAttribute *cell_attribute; - GSList *node; - - g_return_if_fail (GTK_IS_CELL_AREA (area)); - g_return_if_fail (GTK_IS_CELL_RENDERER (renderer)); - g_return_if_fail (attribute != NULL); - g_return_if_fail (gtk_cell_area_has_renderer (area, renderer)); - - info = g_hash_table_lookup (priv->cell_info, renderer); - - if (info) - { - node = g_slist_find_custom (info->attributes, attribute, - (GCompareFunc)cell_attribute_find); - if (node) - { - cell_attribute = node->data; - - cell_attribute_free (cell_attribute); - - info->attributes = g_slist_delete_link (info->attributes, node); - } - } -} - -/** - * gtk_cell_area_attribute_get_column: - * @area: a `GtkCellArea` - * @renderer: a `GtkCellRenderer` - * @attribute: an attribute on the renderer - * - * Returns the model column that an attribute has been mapped to, - * or -1 if the attribute is not mapped. - * - * Returns: the model column, or -1 - */ -int -gtk_cell_area_attribute_get_column (GtkCellArea *area, - GtkCellRenderer *renderer, - const char *attribute) -{ - GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); - CellInfo *info; - CellAttribute *cell_attribute; - GSList *node; - - info = g_hash_table_lookup (priv->cell_info, renderer); - - if (info) - { - node = g_slist_find_custom (info->attributes, attribute, - (GCompareFunc)cell_attribute_find); - if (node) - { - cell_attribute = node->data; - return cell_attribute->column; - } - } - - return -1; -} - -/** - * gtk_cell_area_apply_attributes: - * @area: a `GtkCellArea` - * @tree_model: the `GtkTreeModel` to pull values from - * @iter: the `GtkTreeIter` in @tree_model to apply values for - * @is_expander: whether @iter has children - * @is_expanded: whether @iter is expanded in the view and - * children are visible - * - * Applies any connected attributes to the renderers in - * @area by pulling the values from @tree_model. - */ -void -gtk_cell_area_apply_attributes (GtkCellArea *area, - GtkTreeModel *tree_model, - GtkTreeIter *iter, - gboolean is_expander, - gboolean is_expanded) -{ - g_return_if_fail (GTK_IS_CELL_AREA (area)); - g_return_if_fail (GTK_IS_TREE_MODEL (tree_model)); - g_return_if_fail (iter != NULL); - - g_signal_emit (area, cell_area_signals[SIGNAL_APPLY_ATTRIBUTES], 0, - tree_model, iter, is_expander, is_expanded); -} - -/** - * gtk_cell_area_get_current_path_string: - * @area: a `GtkCellArea` - * - * Gets the current `GtkTreePath` string for the currently - * applied `GtkTreeIter`, this is implicitly updated when - * gtk_cell_area_apply_attributes() is called and can be - * used to interact with renderers from `GtkCellArea` - * subclasses. - * - * Returns: The current `GtkTreePath` string for the current - * attributes applied to @area. This string belongs to the area and - * should not be freed. - */ -const char * -gtk_cell_area_get_current_path_string (GtkCellArea *area) -{ - GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); - - g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL); - - return priv->current_path; -} - - -/************************************************************* - * API: Cell Properties * - *************************************************************/ -/** - * gtk_cell_area_class_install_cell_property: - * @aclass: a `GtkCellAreaClass` - * @property_id: the id for the property - * @pspec: the `GParamSpec` for the property - * - * Installs a cell property on a cell area class. - */ -void -gtk_cell_area_class_install_cell_property (GtkCellAreaClass *aclass, - guint property_id, - GParamSpec *pspec) -{ - g_return_if_fail (GTK_IS_CELL_AREA_CLASS (aclass)); - g_return_if_fail (G_IS_PARAM_SPEC (pspec)); - if (pspec->flags & G_PARAM_WRITABLE) - g_return_if_fail (aclass->set_cell_property != NULL); - if (pspec->flags & G_PARAM_READABLE) - g_return_if_fail (aclass->get_cell_property != NULL); - g_return_if_fail (property_id > 0); - g_return_if_fail (PARAM_SPEC_PARAM_ID (pspec) == 0); /* paranoid */ - g_return_if_fail ((pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)) == 0); - - if (g_param_spec_pool_lookup (cell_property_pool, pspec->name, G_OBJECT_CLASS_TYPE (aclass), TRUE)) - { - g_warning (G_STRLOC ": class '%s' already contains a cell property named '%s'", - G_OBJECT_CLASS_NAME (aclass), pspec->name); - return; - } - g_param_spec_ref (pspec); - g_param_spec_sink (pspec); - PARAM_SPEC_SET_PARAM_ID (pspec, property_id); - g_param_spec_pool_insert (cell_property_pool, pspec, G_OBJECT_CLASS_TYPE (aclass)); -} - -/** - * gtk_cell_area_class_find_cell_property: - * @aclass: a `GtkCellAreaClass` - * @property_name: the name of the child property to find - * - * Finds a cell property of a cell area class by name. - * - * Returns: (transfer none): the `GParamSpec` of the child property - */ -GParamSpec* -gtk_cell_area_class_find_cell_property (GtkCellAreaClass *aclass, - const char *property_name) -{ - g_return_val_if_fail (GTK_IS_CELL_AREA_CLASS (aclass), NULL); - g_return_val_if_fail (property_name != NULL, NULL); - - return g_param_spec_pool_lookup (cell_property_pool, - property_name, - G_OBJECT_CLASS_TYPE (aclass), - TRUE); -} - -/** - * gtk_cell_area_class_list_cell_properties: - * @aclass: a `GtkCellAreaClass` - * @n_properties: (out): location to return the number of cell properties found - * - * Returns all cell properties of a cell area class. - * - * Returns: (array length=n_properties) (transfer container): a newly - * allocated %NULL-terminated array of `GParamSpec`*. The array - * must be freed with g_free(). - */ -GParamSpec** -gtk_cell_area_class_list_cell_properties (GtkCellAreaClass *aclass, - guint *n_properties) -{ - GParamSpec **pspecs; - guint n; - - g_return_val_if_fail (GTK_IS_CELL_AREA_CLASS (aclass), NULL); - - pspecs = g_param_spec_pool_list (cell_property_pool, - G_OBJECT_CLASS_TYPE (aclass), - &n); - if (n_properties) - *n_properties = n; - - return pspecs; -} - -/** - * gtk_cell_area_add_with_properties: - * @area: a `GtkCellArea` - * @renderer: a `GtkCellRenderer` to be placed inside @area - * @first_prop_name: the name of the first cell property to set - * @...: a %NULL-terminated list of property names and values, starting - * with @first_prop_name - * - * Adds @renderer to @area, setting cell properties at the same time. - * See gtk_cell_area_add() and gtk_cell_area_cell_set() for more details. - */ -void -gtk_cell_area_add_with_properties (GtkCellArea *area, - GtkCellRenderer *renderer, - const char *first_prop_name, - ...) -{ - GtkCellAreaClass *class; - - g_return_if_fail (GTK_IS_CELL_AREA (area)); - g_return_if_fail (GTK_IS_CELL_RENDERER (renderer)); - - class = GTK_CELL_AREA_GET_CLASS (area); - - if (class->add) - { - va_list var_args; - - class->add (area, renderer); - - va_start (var_args, first_prop_name); - gtk_cell_area_cell_set_valist (area, renderer, first_prop_name, var_args); - va_end (var_args); - } - else - g_warning ("GtkCellAreaClass::add not implemented for '%s'", - g_type_name (G_TYPE_FROM_INSTANCE (area))); -} - -/** - * gtk_cell_area_cell_set: - * @area: a `GtkCellArea` - * @renderer: a `GtkCellRenderer` which is a cell inside @area - * @first_prop_name: the name of the first cell property to set - * @...: a %NULL-terminated list of property names and values, starting - * with @first_prop_name - * - * Sets one or more cell properties for @cell in @area. - */ -void -gtk_cell_area_cell_set (GtkCellArea *area, - GtkCellRenderer *renderer, - const char *first_prop_name, - ...) -{ - va_list var_args; - - g_return_if_fail (GTK_IS_CELL_AREA (area)); - g_return_if_fail (GTK_IS_CELL_RENDERER (renderer)); - - va_start (var_args, first_prop_name); - gtk_cell_area_cell_set_valist (area, renderer, first_prop_name, var_args); - va_end (var_args); -} - -/** - * gtk_cell_area_cell_get: - * @area: a `GtkCellArea` - * @renderer: a `GtkCellRenderer` which is inside @area - * @first_prop_name: the name of the first cell property to get - * @...: return location for the first cell property, followed - * optionally by more name/return location pairs, followed by %NULL - * - * Gets the values of one or more cell properties for @renderer in @area. - */ -void -gtk_cell_area_cell_get (GtkCellArea *area, - GtkCellRenderer *renderer, - const char *first_prop_name, - ...) -{ - va_list var_args; - - g_return_if_fail (GTK_IS_CELL_AREA (area)); - g_return_if_fail (GTK_IS_CELL_RENDERER (renderer)); - - va_start (var_args, first_prop_name); - gtk_cell_area_cell_get_valist (area, renderer, first_prop_name, var_args); - va_end (var_args); -} - -static inline void -area_get_cell_property (GtkCellArea *area, - GtkCellRenderer *renderer, - GParamSpec *pspec, - GValue *value) -{ - GtkCellAreaClass *class = g_type_class_peek (pspec->owner_type); - - class->get_cell_property (area, renderer, PARAM_SPEC_PARAM_ID (pspec), value, pspec); -} - -static inline void -area_set_cell_property (GtkCellArea *area, - GtkCellRenderer *renderer, - GParamSpec *pspec, - const GValue *value) -{ - GValue tmp_value = G_VALUE_INIT; - GtkCellAreaClass *class = g_type_class_peek (pspec->owner_type); - - /* provide a copy to work from, convert (if necessary) and validate */ - g_value_init (&tmp_value, G_PARAM_SPEC_VALUE_TYPE (pspec)); - if (!g_value_transform (value, &tmp_value)) - g_warning ("unable to set cell property '%s' of type '%s' from value of type '%s'", - pspec->name, - g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)), - G_VALUE_TYPE_NAME (value)); - else if (g_param_value_validate (pspec, &tmp_value) && !(pspec->flags & G_PARAM_LAX_VALIDATION)) - { - char *contents = g_strdup_value_contents (value); - - g_warning ("value \"%s\" of type '%s' is invalid for property '%s' of type '%s'", - contents, - G_VALUE_TYPE_NAME (value), - pspec->name, - g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec))); - g_free (contents); - } - else - { - class->set_cell_property (area, renderer, PARAM_SPEC_PARAM_ID (pspec), &tmp_value, pspec); - } - g_value_unset (&tmp_value); -} - -/** - * gtk_cell_area_cell_set_valist: - * @area: a `GtkCellArea` - * @renderer: a `GtkCellRenderer` which inside @area - * @first_property_name: the name of the first cell property to set - * @var_args: a %NULL-terminated list of property names and values, starting - * with @first_prop_name - * - * Sets one or more cell properties for @renderer in @area. - */ -void -gtk_cell_area_cell_set_valist (GtkCellArea *area, - GtkCellRenderer *renderer, - const char *first_property_name, - va_list var_args) -{ - const char *name; - - g_return_if_fail (GTK_IS_CELL_AREA (area)); - g_return_if_fail (GTK_IS_CELL_RENDERER (renderer)); - - name = first_property_name; - while (name) - { - GValue value = G_VALUE_INIT; - char *error = NULL; - GParamSpec *pspec = - g_param_spec_pool_lookup (cell_property_pool, name, - G_OBJECT_TYPE (area), TRUE); - if (!pspec) - { - g_warning ("%s: cell area class '%s' has no cell property named '%s'", - G_STRLOC, G_OBJECT_TYPE_NAME (area), name); - break; - } - if (!(pspec->flags & G_PARAM_WRITABLE)) - { - g_warning ("%s: cell property '%s' of cell area class '%s' is not writable", - G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area)); - break; - } - - G_VALUE_COLLECT_INIT (&value, G_PARAM_SPEC_VALUE_TYPE (pspec), - var_args, 0, &error); - if (error) - { - g_warning ("%s: %s", G_STRLOC, error); - g_free (error); - - /* we purposely leak the value here, it might not be - * in a sane state if an error condition occurred - */ - break; - } - area_set_cell_property (area, renderer, pspec, &value); - g_value_unset (&value); - name = va_arg (var_args, char *); - } -} - -/** - * gtk_cell_area_cell_get_valist: - * @area: a `GtkCellArea` - * @renderer: a `GtkCellRenderer` inside @area - * @first_property_name: the name of the first property to get - * @var_args: return location for the first property, followed - * optionally by more name/return location pairs, followed by %NULL - * - * Gets the values of one or more cell properties for @renderer in @area. - */ -void -gtk_cell_area_cell_get_valist (GtkCellArea *area, - GtkCellRenderer *renderer, - const char *first_property_name, - va_list var_args) -{ - const char *name; - - g_return_if_fail (GTK_IS_CELL_AREA (area)); - g_return_if_fail (GTK_IS_CELL_RENDERER (renderer)); - - name = first_property_name; - while (name) - { - GValue value = G_VALUE_INIT; - GParamSpec *pspec; - char *error; - - pspec = g_param_spec_pool_lookup (cell_property_pool, name, - G_OBJECT_TYPE (area), TRUE); - if (!pspec) - { - g_warning ("%s: cell area class '%s' has no cell property named '%s'", - G_STRLOC, G_OBJECT_TYPE_NAME (area), name); - break; - } - if (!(pspec->flags & G_PARAM_READABLE)) - { - g_warning ("%s: cell property '%s' of cell area class '%s' is not readable", - G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area)); - break; - } - - g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec)); - area_get_cell_property (area, renderer, pspec, &value); - G_VALUE_LCOPY (&value, var_args, 0, &error); - if (error) - { - g_warning ("%s: %s", G_STRLOC, error); - g_free (error); - g_value_unset (&value); - break; - } - g_value_unset (&value); - name = va_arg (var_args, char *); - } -} - -/** - * gtk_cell_area_cell_set_property: - * @area: a `GtkCellArea` - * @renderer: a `GtkCellRenderer` inside @area - * @property_name: the name of the cell property to set - * @value: the value to set the cell property to - * - * Sets a cell property for @renderer in @area. - */ -void -gtk_cell_area_cell_set_property (GtkCellArea *area, - GtkCellRenderer *renderer, - const char *property_name, - const GValue *value) -{ - GParamSpec *pspec; - - g_return_if_fail (GTK_IS_CELL_AREA (area)); - g_return_if_fail (GTK_IS_CELL_RENDERER (renderer)); - g_return_if_fail (property_name != NULL); - g_return_if_fail (G_IS_VALUE (value)); - - pspec = g_param_spec_pool_lookup (cell_property_pool, property_name, - G_OBJECT_TYPE (area), TRUE); - if (!pspec) - g_warning ("%s: cell area class '%s' has no cell property named '%s'", - G_STRLOC, G_OBJECT_TYPE_NAME (area), property_name); - else if (!(pspec->flags & G_PARAM_WRITABLE)) - g_warning ("%s: cell property '%s' of cell area class '%s' is not writable", - G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area)); - else - { - area_set_cell_property (area, renderer, pspec, value); - } -} - -/** - * gtk_cell_area_cell_get_property: - * @area: a `GtkCellArea` - * @renderer: a `GtkCellRenderer` inside @area - * @property_name: the name of the property to get - * @value: a location to return the value - * - * Gets the value of a cell property for @renderer in @area. - */ -void -gtk_cell_area_cell_get_property (GtkCellArea *area, - GtkCellRenderer *renderer, - const char *property_name, - GValue *value) -{ - GParamSpec *pspec; - - g_return_if_fail (GTK_IS_CELL_AREA (area)); - g_return_if_fail (GTK_IS_CELL_RENDERER (renderer)); - g_return_if_fail (property_name != NULL); - g_return_if_fail (G_IS_VALUE (value)); - - pspec = g_param_spec_pool_lookup (cell_property_pool, property_name, - G_OBJECT_TYPE (area), TRUE); - if (!pspec) - g_warning ("%s: cell area class '%s' has no cell property named '%s'", - G_STRLOC, G_OBJECT_TYPE_NAME (area), property_name); - else if (!(pspec->flags & G_PARAM_READABLE)) - g_warning ("%s: cell property '%s' of cell area class '%s' is not readable", - G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area)); - else - { - GValue *prop_value, tmp_value = G_VALUE_INIT; - - /* auto-conversion of the callers value type - */ - if (G_VALUE_TYPE (value) == G_PARAM_SPEC_VALUE_TYPE (pspec)) - { - g_value_reset (value); - prop_value = value; - } - else if (!g_value_type_transformable (G_PARAM_SPEC_VALUE_TYPE (pspec), G_VALUE_TYPE (value))) - { - g_warning ("can't retrieve cell property '%s' of type '%s' as value of type '%s'", - pspec->name, - g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)), - G_VALUE_TYPE_NAME (value)); - return; - } - else - { - g_value_init (&tmp_value, G_PARAM_SPEC_VALUE_TYPE (pspec)); - prop_value = &tmp_value; - } - - area_get_cell_property (area, renderer, pspec, prop_value); - - if (prop_value != value) - { - g_value_transform (prop_value, value); - g_value_unset (&tmp_value); - } - } -} - -/************************************************************* - * API: Focus * - *************************************************************/ - -/** - * gtk_cell_area_is_activatable: - * @area: a `GtkCellArea` - * - * Returns whether the area can do anything when activated, - * after applying new attributes to @area. - * - * Returns: whether @area can do anything when activated. - */ -gboolean -gtk_cell_area_is_activatable (GtkCellArea *area) -{ - g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE); - - return GTK_CELL_AREA_GET_CLASS (area)->is_activatable (area); -} - -/** - * gtk_cell_area_focus: - * @area: a `GtkCellArea` - * @direction: the `GtkDirectionType` - * - * This should be called by the @area’s owning layout widget - * when focus is to be passed to @area, or moved within @area - * for a given @direction and row data. - * - * Implementing `GtkCellArea` classes should implement this - * method to receive and navigate focus in its own way particular - * to how it lays out cells. - * - * Returns: %TRUE if focus remains inside @area as a result of this call. - */ -gboolean -gtk_cell_area_focus (GtkCellArea *area, - GtkDirectionType direction) -{ - g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE); - - return GTK_CELL_AREA_GET_CLASS (area)->focus (area, direction); -} - -/** - * gtk_cell_area_activate: - * @area: a `GtkCellArea` - * @context: the `GtkCellArea`Context in context with the current row data - * @widget: the `GtkWidget` that @area is rendering on - * @cell_area: the size and location of @area relative to @widget’s allocation - * @flags: the `GtkCellRenderer`State flags for @area for this row of data. - * @edit_only: if %TRUE then only cell renderers that are %GTK_CELL_RENDERER_MODE_EDITABLE - * will be activated. - * - * Activates @area, usually by activating the currently focused - * cell, however some subclasses which embed widgets in the area - * can also activate a widget if it currently has the focus. - * - * Returns: Whether @area was successfully activated. - */ -gboolean -gtk_cell_area_activate (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - const GdkRectangle *cell_area, - GtkCellRendererState flags, - gboolean edit_only) -{ - g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE); - - return GTK_CELL_AREA_GET_CLASS (area)->activate (area, context, widget, cell_area, flags, edit_only); -} - - -/** - * gtk_cell_area_set_focus_cell: - * @area: a `GtkCellArea` - * @renderer: (nullable): the `GtkCellRenderer` to give focus to - * - * Explicitly sets the currently focused cell to @renderer. - * - * This is generally called by implementations of - * `GtkCellAreaClass.focus()` or `GtkCellAreaClass.event()`, - * however it can also be used to implement functions such - * as gtk_tree_view_set_cursor_on_cell(). - */ -void -gtk_cell_area_set_focus_cell (GtkCellArea *area, - GtkCellRenderer *renderer) -{ - GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); - - g_return_if_fail (GTK_IS_CELL_AREA (area)); - g_return_if_fail (renderer == NULL || GTK_IS_CELL_RENDERER (renderer)); - - if (priv->focus_cell != renderer) - { - if (priv->focus_cell) - g_object_unref (priv->focus_cell); - - priv->focus_cell = renderer; - - if (priv->focus_cell) - g_object_ref (priv->focus_cell); - - g_object_notify (G_OBJECT (area), "focus-cell"); - } - - /* Signal that the current focus renderer for this path changed - * (it may be that the focus cell did not change, but the row - * may have changed so we need to signal it) */ - g_signal_emit (area, cell_area_signals[SIGNAL_FOCUS_CHANGED], 0, - priv->focus_cell, priv->current_path); - -} - -/** - * gtk_cell_area_get_focus_cell: - * @area: a `GtkCellArea` - * - * Retrieves the currently focused cell for @area - * - * Returns: (transfer none) (nullable): the currently focused cell in @area. - */ -GtkCellRenderer * -gtk_cell_area_get_focus_cell (GtkCellArea *area) -{ - GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); - - g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL); - - return priv->focus_cell; -} - - -/************************************************************* - * API: Focus Siblings * - *************************************************************/ - -/** - * gtk_cell_area_add_focus_sibling: - * @area: a `GtkCellArea` - * @renderer: the `GtkCellRenderer` expected to have focus - * @sibling: the `GtkCellRenderer` to add to @renderer’s focus area - * - * Adds @sibling to @renderer’s focusable area, focus will be drawn - * around @renderer and all of its siblings if @renderer can - * focus for a given row. - * - * Events handled by focus siblings can also activate the given - * focusable @renderer. - */ -void -gtk_cell_area_add_focus_sibling (GtkCellArea *area, - GtkCellRenderer *renderer, - GtkCellRenderer *sibling) -{ - GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); - GList *siblings; - - g_return_if_fail (GTK_IS_CELL_AREA (area)); - g_return_if_fail (GTK_IS_CELL_RENDERER (renderer)); - g_return_if_fail (GTK_IS_CELL_RENDERER (sibling)); - g_return_if_fail (renderer != sibling); - g_return_if_fail (gtk_cell_area_has_renderer (area, renderer)); - g_return_if_fail (gtk_cell_area_has_renderer (area, sibling)); - g_return_if_fail (!gtk_cell_area_is_focus_sibling (area, renderer, sibling)); - - /* XXX We should also check that sibling is not in any other renderer's sibling - * list already, a renderer can be sibling of only one focusable renderer - * at a time. - */ - - siblings = g_hash_table_lookup (priv->focus_siblings, renderer); - - if (siblings) - { - G_GNUC_UNUSED GList *unused = g_list_append (siblings, sibling); - } - else - { - siblings = g_list_append (siblings, sibling); - g_hash_table_insert (priv->focus_siblings, renderer, siblings); - } -} - -/** - * gtk_cell_area_remove_focus_sibling: - * @area: a `GtkCellArea` - * @renderer: the `GtkCellRenderer` expected to have focus - * @sibling: the `GtkCellRenderer` to remove from @renderer’s focus area - * - * Removes @sibling from @renderer’s focus sibling list - * (see gtk_cell_area_add_focus_sibling()). - */ -void -gtk_cell_area_remove_focus_sibling (GtkCellArea *area, - GtkCellRenderer *renderer, - GtkCellRenderer *sibling) -{ - GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); - GList *siblings; - - g_return_if_fail (GTK_IS_CELL_AREA (area)); - g_return_if_fail (GTK_IS_CELL_RENDERER (renderer)); - g_return_if_fail (GTK_IS_CELL_RENDERER (sibling)); - g_return_if_fail (gtk_cell_area_is_focus_sibling (area, renderer, sibling)); - - siblings = g_hash_table_lookup (priv->focus_siblings, renderer); - - siblings = g_list_copy (siblings); - siblings = g_list_remove (siblings, sibling); - - if (!siblings) - g_hash_table_remove (priv->focus_siblings, renderer); - else - g_hash_table_insert (priv->focus_siblings, renderer, siblings); -} - -/** - * gtk_cell_area_is_focus_sibling: - * @area: a `GtkCellArea` - * @renderer: the `GtkCellRenderer` expected to have focus - * @sibling: the `GtkCellRenderer` to check against @renderer’s sibling list - * - * Returns whether @sibling is one of @renderer’s focus siblings - * (see gtk_cell_area_add_focus_sibling()). - * - * Returns: %TRUE if @sibling is a focus sibling of @renderer - */ -gboolean -gtk_cell_area_is_focus_sibling (GtkCellArea *area, - GtkCellRenderer *renderer, - GtkCellRenderer *sibling) -{ - GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); - GList *siblings, *l; - - g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE); - g_return_val_if_fail (GTK_IS_CELL_RENDERER (renderer), FALSE); - g_return_val_if_fail (GTK_IS_CELL_RENDERER (sibling), FALSE); - - siblings = g_hash_table_lookup (priv->focus_siblings, renderer); - - for (l = siblings; l; l = l->next) - { - GtkCellRenderer *a_sibling = l->data; - - if (a_sibling == sibling) - return TRUE; - } - - return FALSE; -} - -/** - * gtk_cell_area_get_focus_siblings: - * @area: a `GtkCellArea` - * @renderer: the `GtkCellRenderer` expected to have focus - * - * Gets the focus sibling cell renderers for @renderer. - * - * Returns: (element-type GtkCellRenderer) (transfer none): A `GList` of `GtkCellRenderer`s. - * The returned list is internal and should not be freed. - */ -const GList * -gtk_cell_area_get_focus_siblings (GtkCellArea *area, - GtkCellRenderer *renderer) -{ - GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); - - g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL); - g_return_val_if_fail (GTK_IS_CELL_RENDERER (renderer), NULL); - - return g_hash_table_lookup (priv->focus_siblings, renderer); -} - -/** - * gtk_cell_area_get_focus_from_sibling: - * @area: a `GtkCellArea` - * @renderer: the `GtkCellRenderer` - * - * Gets the `GtkCellRenderer` which is expected to be focusable - * for which @renderer is, or may be a sibling. - * - * This is handy for `GtkCellArea` subclasses when handling events, - * after determining the renderer at the event location it can - * then chose to activate the focus cell for which the event - * cell may have been a sibling. - * - * Returns: (nullable) (transfer none): the `GtkCellRenderer` - * for which @renderer is a sibling - */ -GtkCellRenderer * -gtk_cell_area_get_focus_from_sibling (GtkCellArea *area, - GtkCellRenderer *renderer) -{ - GtkCellRenderer *ret_renderer = NULL; - GList *renderers, *l; - - g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL); - g_return_val_if_fail (GTK_IS_CELL_RENDERER (renderer), NULL); - - renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (area)); - - for (l = renderers; l; l = l->next) - { - GtkCellRenderer *a_renderer = l->data; - const GList *list; - - for (list = gtk_cell_area_get_focus_siblings (area, a_renderer); - list; list = list->next) - { - GtkCellRenderer *sibling_renderer = list->data; - - if (sibling_renderer == renderer) - { - ret_renderer = a_renderer; - break; - } - } - } - g_list_free (renderers); - - return ret_renderer; -} - -/************************************************************* - * API: Cell Activation/Editing * - *************************************************************/ -static void -gtk_cell_area_add_editable (GtkCellArea *area, - GtkCellRenderer *renderer, - GtkCellEditable *editable, - const GdkRectangle *cell_area) -{ - GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); - - g_signal_emit (area, cell_area_signals[SIGNAL_ADD_EDITABLE], 0, - renderer, editable, cell_area, priv->current_path); -} - -static void -gtk_cell_area_remove_editable (GtkCellArea *area, - GtkCellRenderer *renderer, - GtkCellEditable *editable) -{ - g_signal_emit (area, cell_area_signals[SIGNAL_REMOVE_EDITABLE], 0, renderer, editable); -} - -static void -cell_area_remove_widget_cb (GtkCellEditable *editable, - GtkCellArea *area) -{ - GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); - - g_assert (priv->edit_widget == editable); - g_assert (priv->edited_cell != NULL); - - gtk_cell_area_remove_editable (area, priv->edited_cell, priv->edit_widget); - - /* Now that we're done with editing the widget and it can be removed, - * remove our references to the widget and disconnect handlers */ - gtk_cell_area_set_edited_cell (area, NULL); - gtk_cell_area_set_edit_widget (area, NULL); -} - -static void -gtk_cell_area_set_edited_cell (GtkCellArea *area, - GtkCellRenderer *renderer) -{ - GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); - - g_return_if_fail (GTK_IS_CELL_AREA (area)); - g_return_if_fail (renderer == NULL || GTK_IS_CELL_RENDERER (renderer)); - - if (priv->edited_cell != renderer) - { - if (priv->edited_cell) - g_object_unref (priv->edited_cell); - - priv->edited_cell = renderer; - - if (priv->edited_cell) - g_object_ref (priv->edited_cell); - - g_object_notify (G_OBJECT (area), "edited-cell"); - } -} - -static void -gtk_cell_area_set_edit_widget (GtkCellArea *area, - GtkCellEditable *editable) -{ - GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); - - g_return_if_fail (GTK_IS_CELL_AREA (area)); - g_return_if_fail (editable == NULL || GTK_IS_CELL_EDITABLE (editable)); - - if (priv->edit_widget != editable) - { - if (priv->edit_widget) - { - g_signal_handler_disconnect (priv->edit_widget, priv->remove_widget_id); - - g_object_unref (priv->edit_widget); - } - - priv->edit_widget = editable; - - if (priv->edit_widget) - { - priv->remove_widget_id = - g_signal_connect (priv->edit_widget, "remove-widget", - G_CALLBACK (cell_area_remove_widget_cb), area); - - g_object_ref (priv->edit_widget); - } - - g_object_notify (G_OBJECT (area), "edit-widget"); - } -} - -/** - * gtk_cell_area_get_edited_cell: - * @area: a `GtkCellArea` - * - * Gets the `GtkCellRenderer` in @area that is currently - * being edited. - * - * Returns: (transfer none) (nullable): The currently edited `GtkCellRenderer` - */ -GtkCellRenderer * -gtk_cell_area_get_edited_cell (GtkCellArea *area) -{ - GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); - - g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL); - - return priv->edited_cell; -} - -/** - * gtk_cell_area_get_edit_widget: - * @area: a `GtkCellArea` - * - * Gets the `GtkCellEditable` widget currently used - * to edit the currently edited cell. - * - * Returns: (transfer none) (nullable): The currently active `GtkCellEditable` widget - */ -GtkCellEditable * -gtk_cell_area_get_edit_widget (GtkCellArea *area) -{ - GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); - - g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL); - - return priv->edit_widget; -} - -/** - * gtk_cell_area_activate_cell: - * @area: a `GtkCellArea` - * @widget: the `GtkWidget` that @area is rendering onto - * @renderer: the `GtkCellRenderer` in @area to activate - * @event: the `GdkEvent` for which cell activation should occur - * @cell_area: the `GdkRectangle` in @widget relative coordinates - * of @renderer for the current row. - * @flags: the `GtkCellRenderer`State for @renderer - * - * This is used by `GtkCellArea` subclasses when handling events - * to activate cells, the base `GtkCellArea` class activates cells - * for keyboard events for free in its own GtkCellArea->activate() - * implementation. - * - * Returns: whether cell activation was successful - */ -gboolean -gtk_cell_area_activate_cell (GtkCellArea *area, - GtkWidget *widget, - GtkCellRenderer *renderer, - GdkEvent *event, - const GdkRectangle *cell_area, - GtkCellRendererState flags) -{ - GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); - GtkCellRendererMode mode; - - g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE); - g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); - g_return_val_if_fail (GTK_IS_CELL_RENDERER (renderer), FALSE); - g_return_val_if_fail (cell_area != NULL, FALSE); - - if (!gtk_cell_renderer_get_sensitive (renderer)) - return FALSE; - - g_object_get (renderer, "mode", &mode, NULL); - - if (mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE) - { - if (gtk_cell_renderer_activate (renderer, - event, widget, - priv->current_path, - cell_area, - cell_area, - flags)) - return TRUE; - } - else if (mode == GTK_CELL_RENDERER_MODE_EDITABLE) - { - GtkCellEditable *editable_widget; - GdkRectangle inner_area; - - gtk_cell_area_inner_cell_area (area, widget, cell_area, &inner_area); - - editable_widget = - gtk_cell_renderer_start_editing (renderer, - event, widget, - priv->current_path, - &inner_area, - &inner_area, - flags); - - if (editable_widget != NULL) - { - g_return_val_if_fail (GTK_IS_CELL_EDITABLE (editable_widget), FALSE); - - gtk_cell_area_set_edited_cell (area, renderer); - gtk_cell_area_set_edit_widget (area, editable_widget); - - /* Signal that editing started so that callers can get - * a handle on the editable_widget */ - gtk_cell_area_add_editable (area, priv->focus_cell, editable_widget, cell_area); - - /* If the signal was successfully handled start the editing */ - if (gtk_widget_get_parent (GTK_WIDGET (editable_widget))) - { - gtk_cell_editable_start_editing (editable_widget, event); - gtk_widget_grab_focus (GTK_WIDGET (editable_widget)); - } - else - { - /* Otherwise clear the editing state and fire a warning */ - gtk_cell_area_set_edited_cell (area, NULL); - gtk_cell_area_set_edit_widget (area, NULL); - - g_warning ("GtkCellArea::add-editable fired in the dark, no cell editing was started."); - } - - return TRUE; - } - } - - return FALSE; -} - -/** - * gtk_cell_area_stop_editing: - * @area: a `GtkCellArea` - * @canceled: whether editing was canceled. - * - * Explicitly stops the editing of the currently edited cell. - * - * If @canceled is %TRUE, the currently edited cell renderer - * will emit the ::editing-canceled signal, otherwise the - * the ::editing-done signal will be emitted on the current - * edit widget. - * - * See gtk_cell_area_get_edited_cell() and gtk_cell_area_get_edit_widget(). - */ -void -gtk_cell_area_stop_editing (GtkCellArea *area, - gboolean canceled) -{ - GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); - - g_return_if_fail (GTK_IS_CELL_AREA (area)); - - if (priv->edited_cell) - { - GtkCellEditable *edit_widget = g_object_ref (priv->edit_widget); - GtkCellRenderer *edit_cell = g_object_ref (priv->edited_cell); - - /* Stop editing of the cell renderer */ - gtk_cell_renderer_stop_editing (priv->edited_cell, canceled); - - /* When editing is explicitly halted either - * the "editing-canceled" signal is emitted on the cell - * renderer or the "editing-done" signal on the GtkCellEditable widget - */ - if (!canceled) - gtk_cell_editable_editing_done (edit_widget); - - /* Remove any references to the editable widget */ - gtk_cell_area_set_edited_cell (area, NULL); - gtk_cell_area_set_edit_widget (area, NULL); - - /* Send the remove-widget signal explicitly (this is done after setting - * the edit cell/widget NULL to avoid feedback) - */ - gtk_cell_area_remove_editable (area, edit_cell, edit_widget); - g_object_unref (edit_cell); - g_object_unref (edit_widget); - } -} - -/************************************************************* - * API: Convenience for area implementations * - *************************************************************/ - -/** - * gtk_cell_area_inner_cell_area: - * @area: a `GtkCellArea` - * @widget: the `GtkWidget` that @area is rendering onto - * @cell_area: the @widget relative coordinates where one of @area’s cells - * is to be placed - * @inner_area: (out): the return location for the inner cell area - * - * This is a convenience function for `GtkCellArea` implementations - * to get the inner area where a given `GtkCellRenderer` will be - * rendered. It removes any padding previously added by gtk_cell_area_request_renderer(). - */ -void -gtk_cell_area_inner_cell_area (GtkCellArea *area, - GtkWidget *widget, - const GdkRectangle *cell_area, - GdkRectangle *inner_area) -{ - GtkBorder border; - GtkStyleContext *context; - - g_return_if_fail (GTK_IS_CELL_AREA (area)); - g_return_if_fail (GTK_IS_WIDGET (widget)); - g_return_if_fail (cell_area != NULL); - g_return_if_fail (inner_area != NULL); - - context = gtk_widget_get_style_context (widget); - gtk_style_context_get_padding (context, &border); - - *inner_area = *cell_area; - - if (border.left + border.right > cell_area->width) - { - border.left = cell_area->width / 2; - border.right = (cell_area->width + 1) / 2; - } - inner_area->x += border.left; - inner_area->width -= border.left + border.right; - if (border.top + border.bottom > cell_area->height) - { - border.top = cell_area->height / 2; - border.bottom = (cell_area->height + 1) / 2; - } - inner_area->y += border.top; - inner_area->height -= border.top + border.bottom; -} - -/** - * gtk_cell_area_request_renderer: - * @area: a `GtkCellArea` - * @renderer: the `GtkCellRenderer` to request size for - * @orientation: the `GtkOrientation` in which to request size - * @widget: the `GtkWidget` that @area is rendering onto - * @for_size: the allocation contextual size to request for, or -1 if - * the base request for the orientation is to be returned. - * @minimum_size: (out) (optional): location to store the minimum size - * @natural_size: (out) (optional): location to store the natural size - * - * This is a convenience function for `GtkCellArea` implementations - * to request size for cell renderers. It’s important to use this - * function to request size and then use gtk_cell_area_inner_cell_area() - * at render and event time since this function will add padding - * around the cell for focus painting. - */ -void -gtk_cell_area_request_renderer (GtkCellArea *area, - GtkCellRenderer *renderer, - GtkOrientation orientation, - GtkWidget *widget, - int for_size, - int *minimum_size, - int *natural_size) -{ - GtkBorder border; - GtkStyleContext *context; - - g_return_if_fail (GTK_IS_CELL_AREA (area)); - g_return_if_fail (GTK_IS_CELL_RENDERER (renderer)); - g_return_if_fail (GTK_IS_WIDGET (widget)); - g_return_if_fail (minimum_size != NULL); - g_return_if_fail (natural_size != NULL); - - context = gtk_widget_get_style_context (widget); - gtk_style_context_get_padding (context, &border); - - if (orientation == GTK_ORIENTATION_HORIZONTAL) - { - if (for_size < 0) - gtk_cell_renderer_get_preferred_width (renderer, widget, minimum_size, natural_size); - else - { - for_size = MAX (0, for_size - border.left - border.right); - - gtk_cell_renderer_get_preferred_width_for_height (renderer, widget, for_size, - minimum_size, natural_size); - } - - *minimum_size += border.left + border.right; - *natural_size += border.left + border.right; - } - else /* GTK_ORIENTATION_VERTICAL */ - { - if (for_size < 0) - gtk_cell_renderer_get_preferred_height (renderer, widget, minimum_size, natural_size); - else - { - for_size = MAX (0, for_size - border.top - border.bottom); - - gtk_cell_renderer_get_preferred_height_for_width (renderer, widget, for_size, - minimum_size, natural_size); - } - - *minimum_size += border.top + border.bottom; - *natural_size += border.top + border.bottom; - } -} - -void -_gtk_cell_area_set_cell_data_func_with_proxy (GtkCellArea *area, - GtkCellRenderer *cell, - GFunc func, - gpointer func_data, - GDestroyNotify destroy, - gpointer proxy) -{ - GtkCellAreaPrivate *priv = gtk_cell_area_get_instance_private (area); - CellInfo *info; - - g_return_if_fail (GTK_IS_CELL_AREA (area)); - g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); - - info = g_hash_table_lookup (priv->cell_info, cell); - - /* Note we do not take a reference to the proxy, the proxy is a GtkCellLayout - * that is forwarding its implementation to a delegate GtkCellArea therefore - * its life-cycle is longer than the area's life cycle. - */ - if (info) - { - if (info->destroy && info->data) - info->destroy (info->data); - - if (func) - { - info->func = (GtkCellLayoutDataFunc)func; - info->data = func_data; - info->destroy = destroy; - info->proxy = proxy; - } - else - { - info->func = NULL; - info->data = NULL; - info->destroy = NULL; - info->proxy = NULL; - } - } - else - { - info = cell_info_new ((GtkCellLayoutDataFunc)func, func_data, destroy); - info->proxy = proxy; - - g_hash_table_insert (priv->cell_info, cell, info); - } -} diff --git a/gtk/gtkcellarea.h b/gtk/gtkcellarea.h deleted file mode 100644 index 85ddaf0616..0000000000 --- a/gtk/gtkcellarea.h +++ /dev/null @@ -1,525 +0,0 @@ -/* gtkcellarea.h - * - * Copyright (C) 2010 Openismus GmbH - * - * Authors: - * Tristan Van Berkom - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#ifndef __GTK_CELL_AREA_H__ -#define __GTK_CELL_AREA_H__ - -#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) -#error "Only can be included directly." -#endif - -#include -#include -#include - -G_BEGIN_DECLS - -#define GTK_TYPE_CELL_AREA (gtk_cell_area_get_type ()) -#define GTK_CELL_AREA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_AREA, GtkCellArea)) -#define GTK_CELL_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_CELL_AREA, GtkCellAreaClass)) -#define GTK_IS_CELL_AREA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_AREA)) -#define GTK_IS_CELL_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_CELL_AREA)) -#define GTK_CELL_AREA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CELL_AREA, GtkCellAreaClass)) - -typedef struct _GtkCellArea GtkCellArea; -typedef struct _GtkCellAreaClass GtkCellAreaClass; -typedef struct _GtkCellAreaContext GtkCellAreaContext; - -/** - * GTK_CELL_AREA_WARN_INVALID_CELL_PROPERTY_ID: - * @object: the `GObject` on which set_cell_property() or get_cell_property() - * was called - * @property_id: the numeric id of the property - * @pspec: the `GParamSpec` of the property - * - * This macro should be used to emit a standard warning about unexpected - * properties in set_cell_property() and get_cell_property() implementations. - */ -#define GTK_CELL_AREA_WARN_INVALID_CELL_PROPERTY_ID(object, property_id, pspec) \ - G_OBJECT_WARN_INVALID_PSPEC ((object), "cell property id", (property_id), (pspec)) - -/** - * GtkCellCallback: - * @renderer: the cell renderer to operate on - * @data: (closure): user-supplied data - * - * The type of the callback functions used for iterating over - * the cell renderers of a `GtkCellArea`, see gtk_cell_area_foreach(). - * - * Returns: %TRUE to stop iterating over cells. - */ -typedef gboolean (*GtkCellCallback) (GtkCellRenderer *renderer, - gpointer data); - -/** - * GtkCellAllocCallback: - * @renderer: the cell renderer to operate on - * @cell_area: the area allocated to @renderer inside the rectangle - * provided to gtk_cell_area_foreach_alloc(). - * @cell_background: the background area for @renderer inside the - * background area provided to gtk_cell_area_foreach_alloc(). - * @data: (closure): user-supplied data - * - * The type of the callback functions used for iterating over the - * cell renderers and their allocated areas inside a `GtkCellArea`, - * see gtk_cell_area_foreach_alloc(). - * - * Returns: %TRUE to stop iterating over cells. - */ -typedef gboolean (*GtkCellAllocCallback) (GtkCellRenderer *renderer, - const GdkRectangle *cell_area, - const GdkRectangle *cell_background, - gpointer data); - - -struct _GtkCellArea -{ - /*< private >*/ - GInitiallyUnowned parent_instance; -}; - - -/** - * GtkCellAreaClass: - * @add: adds a `GtkCellRenderer` to the area. - * @remove: removes a `GtkCellRenderer` from the area. - * @foreach: calls the `GtkCellCallback` function on every `GtkCellRenderer` in - * the area with the provided user data until the callback returns %TRUE. - * @foreach_alloc: Calls the `GtkCellAllocCallback` function on every - * `GtkCellRenderer` in the area with the allocated area for the cell - * and the provided user data until the callback returns %TRUE. - * @event: Handle an event in the area, this is generally used to activate - * a cell at the event location for button events but can also be used - * to generically pass events to `GtkWidget`s drawn onto the area. - * @snapshot: Actually snapshot the area’s cells to the specified rectangle, - * @background_area should be correctly distributed to the cells - * corresponding background areas. - * @apply_attributes: Apply the cell attributes to the cells. This is - * implemented as a signal and generally `GtkCellArea` subclasses don't - * need to implement it since it is handled by the base class. - * @create_context: Creates and returns a class specific `GtkCellAreaContext` - * to store cell alignment and allocation details for a said `GtkCellArea` - * class. - * @copy_context: Creates a new `GtkCellAreaContext` in the same state as - * the passed @context with any cell alignment data and allocations intact. - * @get_request_mode: This allows an area to tell its layouting widget whether - * it prefers to be allocated in %GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH or - * %GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT mode. - * @get_preferred_width: Calculates the minimum and natural width of the - * areas cells with the current attributes applied while considering - * the particular layouting details of the said `GtkCellArea`. While - * requests are performed over a series of rows, alignments and overall - * minimum and natural sizes should be stored in the corresponding - * `GtkCellAreaContext`. - * @get_preferred_height_for_width: Calculates the minimum and natural height - * for the area if the passed @context would be allocated the given width. - * When implementing this virtual method it is safe to assume that @context - * has already stored the aligned cell widths for every `GtkTreeModel` row - * that @context will be allocated for since this information was stored - * at `GtkCellAreaClass.get_preferred_width()` time. This virtual method - * should also store any necessary alignments of cell heights for the - * case that the context is allocated a height. - * @get_preferred_height: Calculates the minimum and natural height of the - * areas cells with the current attributes applied. Essentially this is - * the same as `GtkCellAreaClass.get_preferred_width()` only for areas - * that are being requested as %GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT. - * @get_preferred_width_for_height: Calculates the minimum and natural width - * for the area if the passed @context would be allocated the given - * height. The same as `GtkCellAreaClass.get_preferred_height_for_width()` - * only for handling requests in the %GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT - * mode. - * @set_cell_property: This should be implemented to handle changes in child - * cell properties for a given `GtkCellRenderer` that were previously - * installed on the `GtkCellAreaClass` with gtk_cell_area_class_install_cell_property(). - * @get_cell_property: This should be implemented to report the values of - * child cell properties for a given child `GtkCellRenderer`. - * @focus: This virtual method should be implemented to navigate focus from - * cell to cell inside the `GtkCellArea`. The `GtkCellArea` should move - * focus from cell to cell inside the area and return %FALSE if focus - * logically leaves the area with the following exceptions: When the - * area contains no activatable cells, the entire area receives focus. - * Focus should not be given to cells that are actually “focus siblings” - * of other sibling cells (see gtk_cell_area_get_focus_from_sibling()). - * Focus is set by calling gtk_cell_area_set_focus_cell(). - * @is_activatable: Returns whether the `GtkCellArea` can respond to - * `GtkCellAreaClass.activate()`, usually this does not need to be - * implemented since the base class takes care of this however it can - * be enhanced if the `GtkCellArea` subclass can handle activation in - * other ways than activating its `GtkCellRenderers`. - * @activate: This is called when the layouting widget rendering the - * `GtkCellArea` activates the focus cell (see gtk_cell_area_get_focus_cell()). - */ -struct _GtkCellAreaClass -{ - /*< private >*/ - GInitiallyUnownedClass parent_class; - - /*< public >*/ - - /* Basic methods */ - void (* add) (GtkCellArea *area, - GtkCellRenderer *renderer); - void (* remove) (GtkCellArea *area, - GtkCellRenderer *renderer); - void (* foreach) (GtkCellArea *area, - GtkCellCallback callback, - gpointer callback_data); - void (* foreach_alloc) (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - const GdkRectangle *cell_area, - const GdkRectangle *background_area, - GtkCellAllocCallback callback, - gpointer callback_data); - int (* event) (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - GdkEvent *event, - const GdkRectangle *cell_area, - GtkCellRendererState flags); - void (* snapshot) (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - GtkSnapshot *snapshot, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - GtkCellRendererState flags, - gboolean paint_focus); - void (* apply_attributes) (GtkCellArea *area, - GtkTreeModel *tree_model, - GtkTreeIter *iter, - gboolean is_expander, - gboolean is_expanded); - - /* Geometry */ - GtkCellAreaContext *(* create_context) (GtkCellArea *area); - GtkCellAreaContext *(* copy_context) (GtkCellArea *area, - GtkCellAreaContext *context); - GtkSizeRequestMode (* get_request_mode) (GtkCellArea *area); - void (* get_preferred_width) (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - int *minimum_width, - int *natural_width); - void (* get_preferred_height_for_width) (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - int width, - int *minimum_height, - int *natural_height); - void (* get_preferred_height) (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - int *minimum_height, - int *natural_height); - void (* get_preferred_width_for_height) (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - int height, - int *minimum_width, - int *natural_width); - - /* Cell Properties */ - void (* set_cell_property) (GtkCellArea *area, - GtkCellRenderer *renderer, - guint property_id, - const GValue *value, - GParamSpec *pspec); - void (* get_cell_property) (GtkCellArea *area, - GtkCellRenderer *renderer, - guint property_id, - GValue *value, - GParamSpec *pspec); - - /* Focus */ - gboolean (* focus) (GtkCellArea *area, - GtkDirectionType direction); - gboolean (* is_activatable) (GtkCellArea *area); - gboolean (* activate) (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - const GdkRectangle *cell_area, - GtkCellRendererState flags, - gboolean edit_only); - - /*< private >*/ - - gpointer padding[8]; -}; - -GDK_AVAILABLE_IN_ALL -GType gtk_cell_area_get_type (void) G_GNUC_CONST; - -/* Basic methods */ -GDK_AVAILABLE_IN_ALL -void gtk_cell_area_add (GtkCellArea *area, - GtkCellRenderer *renderer); -GDK_AVAILABLE_IN_ALL -void gtk_cell_area_remove (GtkCellArea *area, - GtkCellRenderer *renderer); -GDK_AVAILABLE_IN_ALL -gboolean gtk_cell_area_has_renderer (GtkCellArea *area, - GtkCellRenderer *renderer); -GDK_AVAILABLE_IN_ALL -void gtk_cell_area_foreach (GtkCellArea *area, - GtkCellCallback callback, - gpointer callback_data); -GDK_AVAILABLE_IN_ALL -void gtk_cell_area_foreach_alloc (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - const GdkRectangle *cell_area, - const GdkRectangle *background_area, - GtkCellAllocCallback callback, - gpointer callback_data); -GDK_AVAILABLE_IN_ALL -int gtk_cell_area_event (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - GdkEvent *event, - const GdkRectangle *cell_area, - GtkCellRendererState flags); -GDK_AVAILABLE_IN_ALL -void gtk_cell_area_snapshot (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - GtkSnapshot *snapshot, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - GtkCellRendererState flags, - gboolean paint_focus); - -GDK_AVAILABLE_IN_ALL -void gtk_cell_area_get_cell_allocation (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - GtkCellRenderer *renderer, - const GdkRectangle *cell_area, - GdkRectangle *allocation); -GDK_AVAILABLE_IN_ALL -GtkCellRenderer *gtk_cell_area_get_cell_at_position (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - const GdkRectangle *cell_area, - int x, - int y, - GdkRectangle *alloc_area); - -/* Geometry */ -GDK_AVAILABLE_IN_ALL -GtkCellAreaContext *gtk_cell_area_create_context (GtkCellArea *area); -GDK_AVAILABLE_IN_ALL -GtkCellAreaContext *gtk_cell_area_copy_context (GtkCellArea *area, - GtkCellAreaContext *context); -GDK_AVAILABLE_IN_ALL -GtkSizeRequestMode gtk_cell_area_get_request_mode (GtkCellArea *area); -GDK_AVAILABLE_IN_ALL -void gtk_cell_area_get_preferred_width (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - int *minimum_width, - int *natural_width); -GDK_AVAILABLE_IN_ALL -void gtk_cell_area_get_preferred_height_for_width (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - int width, - int *minimum_height, - int *natural_height); -GDK_AVAILABLE_IN_ALL -void gtk_cell_area_get_preferred_height (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - int *minimum_height, - int *natural_height); -GDK_AVAILABLE_IN_ALL -void gtk_cell_area_get_preferred_width_for_height (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - int height, - int *minimum_width, - int *natural_width); -GDK_AVAILABLE_IN_ALL -const char * gtk_cell_area_get_current_path_string (GtkCellArea *area); - - -/* Attributes */ -GDK_AVAILABLE_IN_ALL -void gtk_cell_area_apply_attributes (GtkCellArea *area, - GtkTreeModel *tree_model, - GtkTreeIter *iter, - gboolean is_expander, - gboolean is_expanded); -GDK_AVAILABLE_IN_ALL -void gtk_cell_area_attribute_connect (GtkCellArea *area, - GtkCellRenderer *renderer, - const char *attribute, - int column); -GDK_AVAILABLE_IN_ALL -void gtk_cell_area_attribute_disconnect (GtkCellArea *area, - GtkCellRenderer *renderer, - const char *attribute); -GDK_AVAILABLE_IN_ALL -int gtk_cell_area_attribute_get_column (GtkCellArea *area, - GtkCellRenderer *renderer, - const char *attribute); - - -/* Cell Properties */ -GDK_AVAILABLE_IN_ALL -void gtk_cell_area_class_install_cell_property (GtkCellAreaClass *aclass, - guint property_id, - GParamSpec *pspec); -GDK_AVAILABLE_IN_ALL -GParamSpec* gtk_cell_area_class_find_cell_property (GtkCellAreaClass *aclass, - const char *property_name); -GDK_AVAILABLE_IN_ALL -GParamSpec** gtk_cell_area_class_list_cell_properties (GtkCellAreaClass *aclass, - guint *n_properties); -GDK_AVAILABLE_IN_ALL -void gtk_cell_area_add_with_properties (GtkCellArea *area, - GtkCellRenderer *renderer, - const char *first_prop_name, - ...) G_GNUC_NULL_TERMINATED; -GDK_AVAILABLE_IN_ALL -void gtk_cell_area_cell_set (GtkCellArea *area, - GtkCellRenderer *renderer, - const char *first_prop_name, - ...) G_GNUC_NULL_TERMINATED; -GDK_AVAILABLE_IN_ALL -void gtk_cell_area_cell_get (GtkCellArea *area, - GtkCellRenderer *renderer, - const char *first_prop_name, - ...) G_GNUC_NULL_TERMINATED; -GDK_AVAILABLE_IN_ALL -void gtk_cell_area_cell_set_valist (GtkCellArea *area, - GtkCellRenderer *renderer, - const char *first_property_name, - va_list var_args); -GDK_AVAILABLE_IN_ALL -void gtk_cell_area_cell_get_valist (GtkCellArea *area, - GtkCellRenderer *renderer, - const char *first_property_name, - va_list var_args); -GDK_AVAILABLE_IN_ALL -void gtk_cell_area_cell_set_property (GtkCellArea *area, - GtkCellRenderer *renderer, - const char *property_name, - const GValue *value); -GDK_AVAILABLE_IN_ALL -void gtk_cell_area_cell_get_property (GtkCellArea *area, - GtkCellRenderer *renderer, - const char *property_name, - GValue *value); - -/* Focus */ -GDK_AVAILABLE_IN_ALL -gboolean gtk_cell_area_is_activatable (GtkCellArea *area); -GDK_AVAILABLE_IN_ALL -gboolean gtk_cell_area_activate (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - const GdkRectangle *cell_area, - GtkCellRendererState flags, - gboolean edit_only); -GDK_AVAILABLE_IN_ALL -gboolean gtk_cell_area_focus (GtkCellArea *area, - GtkDirectionType direction); -GDK_AVAILABLE_IN_ALL -void gtk_cell_area_set_focus_cell (GtkCellArea *area, - GtkCellRenderer *renderer); -GDK_AVAILABLE_IN_ALL -GtkCellRenderer *gtk_cell_area_get_focus_cell (GtkCellArea *area); - - -/* Focus siblings */ -GDK_AVAILABLE_IN_ALL -void gtk_cell_area_add_focus_sibling (GtkCellArea *area, - GtkCellRenderer *renderer, - GtkCellRenderer *sibling); -GDK_AVAILABLE_IN_ALL -void gtk_cell_area_remove_focus_sibling (GtkCellArea *area, - GtkCellRenderer *renderer, - GtkCellRenderer *sibling); -GDK_AVAILABLE_IN_ALL -gboolean gtk_cell_area_is_focus_sibling (GtkCellArea *area, - GtkCellRenderer *renderer, - GtkCellRenderer *sibling); -GDK_AVAILABLE_IN_ALL -const GList * gtk_cell_area_get_focus_siblings (GtkCellArea *area, - GtkCellRenderer *renderer); -GDK_AVAILABLE_IN_ALL -GtkCellRenderer *gtk_cell_area_get_focus_from_sibling (GtkCellArea *area, - GtkCellRenderer *renderer); - -/* Cell Activation/Editing */ -GDK_AVAILABLE_IN_ALL -GtkCellRenderer *gtk_cell_area_get_edited_cell (GtkCellArea *area); -GDK_AVAILABLE_IN_ALL -GtkCellEditable *gtk_cell_area_get_edit_widget (GtkCellArea *area); -GDK_AVAILABLE_IN_ALL -gboolean gtk_cell_area_activate_cell (GtkCellArea *area, - GtkWidget *widget, - GtkCellRenderer *renderer, - GdkEvent *event, - const GdkRectangle *cell_area, - GtkCellRendererState flags); -GDK_AVAILABLE_IN_ALL -void gtk_cell_area_stop_editing (GtkCellArea *area, - gboolean canceled); - -/* Functions for area implementations */ - -/* Distinguish the inner cell area from the whole requested area including margins */ -GDK_AVAILABLE_IN_ALL -void gtk_cell_area_inner_cell_area (GtkCellArea *area, - GtkWidget *widget, - const GdkRectangle *cell_area, - GdkRectangle *inner_area); - -/* Request the size of a cell while respecting the cell margins (requests are margin inclusive) */ -GDK_AVAILABLE_IN_ALL -void gtk_cell_area_request_renderer (GtkCellArea *area, - GtkCellRenderer *renderer, - GtkOrientation orientation, - GtkWidget *widget, - int for_size, - int *minimum_size, - int *natural_size); - -/* For api stability, this is called from gtkcelllayout.c in order to ensure the correct - * object is passed to the user function in gtk_cell_layout_set_cell_data_func. - * - * This private api takes gpointer & GFunc arguments to circumvent circular header file - * dependencies. - */ -void _gtk_cell_area_set_cell_data_func_with_proxy (GtkCellArea *area, - GtkCellRenderer *cell, - GFunc func, - gpointer func_data, - GDestroyNotify destroy, - gpointer proxy); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkCellArea, g_object_unref) - -G_END_DECLS - -#endif /* __GTK_CELL_AREA_H__ */ diff --git a/gtk/gtkcellareabox.c b/gtk/gtkcellareabox.c deleted file mode 100644 index cfacb38866..0000000000 --- a/gtk/gtkcellareabox.c +++ /dev/null @@ -1,2237 +0,0 @@ -/* gtkcellareabox.c - * - * Copyright (C) 2010 Openismus GmbH - * - * Authors: - * Tristan Van Berkom - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - - -/** - * GtkCellAreaBox: - * - * A cell area that renders GtkCellRenderers into a row or a column - * - * The `GtkCellAreaBox` renders cell renderers into a row or a column - * depending on its `GtkOrientation`. - * - * GtkCellAreaBox uses a notion of packing. Packing - * refers to adding cell renderers with reference to a particular position - * in a `GtkCellAreaBox`. There are two reference positions: the - * start and the end of the box. - * When the `GtkCellAreaBox` is oriented in the %GTK_ORIENTATION_VERTICAL - * orientation, the start is defined as the top of the box and the end is - * defined as the bottom. In the %GTK_ORIENTATION_HORIZONTAL orientation - * start is defined as the left side and the end is defined as the right - * side. - * - * Alignments of `GtkCellRenderer`s rendered in adjacent rows can be - * configured by configuring the `GtkCellAreaBox` align child cell property - * with gtk_cell_area_cell_set_property() or by specifying the "align" - * argument to gtk_cell_area_box_pack_start() and gtk_cell_area_box_pack_end(). - */ - -#include "config.h" -#include "gtkorientable.h" -#include "gtkcelllayout.h" -#include "gtkcellareabox.h" -#include "gtkcellareaboxcontextprivate.h" -#include "gtktypebuiltins.h" -#include "gtkprivate.h" - - -/* GObjectClass */ -static void gtk_cell_area_box_finalize (GObject *object); -static void gtk_cell_area_box_dispose (GObject *object); -static void gtk_cell_area_box_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec); -static void gtk_cell_area_box_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec); - -/* GtkCellAreaClass */ -static void gtk_cell_area_box_add (GtkCellArea *area, - GtkCellRenderer *renderer); -static void gtk_cell_area_box_remove (GtkCellArea *area, - GtkCellRenderer *renderer); -static void gtk_cell_area_box_foreach (GtkCellArea *area, - GtkCellCallback callback, - gpointer callback_data); -static void gtk_cell_area_box_foreach_alloc (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - const GdkRectangle *cell_area, - const GdkRectangle *background_area, - GtkCellAllocCallback callback, - gpointer callback_data); -static void gtk_cell_area_box_apply_attributes (GtkCellArea *area, - GtkTreeModel *tree_model, - GtkTreeIter *iter, - gboolean is_expander, - gboolean is_expanded); -static void gtk_cell_area_box_set_cell_property (GtkCellArea *area, - GtkCellRenderer *renderer, - guint prop_id, - const GValue *value, - GParamSpec *pspec); -static void gtk_cell_area_box_get_cell_property (GtkCellArea *area, - GtkCellRenderer *renderer, - guint prop_id, - GValue *value, - GParamSpec *pspec); -static GtkCellAreaContext *gtk_cell_area_box_create_context (GtkCellArea *area); -static GtkCellAreaContext *gtk_cell_area_box_copy_context (GtkCellArea *area, - GtkCellAreaContext *context); -static GtkSizeRequestMode gtk_cell_area_box_get_request_mode (GtkCellArea *area); -static void gtk_cell_area_box_get_preferred_width (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - int *minimum_width, - int *natural_width); -static void gtk_cell_area_box_get_preferred_height (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - int *minimum_height, - int *natural_height); -static void gtk_cell_area_box_get_preferred_height_for_width (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - int width, - int *minimum_height, - int *natural_height); -static void gtk_cell_area_box_get_preferred_width_for_height (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - int height, - int *minimum_width, - int *natural_width); -static gboolean gtk_cell_area_box_focus (GtkCellArea *area, - GtkDirectionType direction); - -/* GtkCellLayoutIface */ -static void gtk_cell_area_box_cell_layout_init (GtkCellLayoutIface *iface); -static void gtk_cell_area_box_layout_pack_start (GtkCellLayout *cell_layout, - GtkCellRenderer *renderer, - gboolean expand); -static void gtk_cell_area_box_layout_pack_end (GtkCellLayout *cell_layout, - GtkCellRenderer *renderer, - gboolean expand); -static void gtk_cell_area_box_layout_reorder (GtkCellLayout *cell_layout, - GtkCellRenderer *renderer, - int position); -static void gtk_cell_area_box_focus_changed (GtkCellArea *area, - GParamSpec *pspec, - GtkCellAreaBox *box); - - -/* CellInfo/CellGroup metadata handling and convenience functions */ -typedef struct { - GtkCellRenderer *renderer; - - guint expand : 1; /* Whether the cell expands */ - guint pack : 1; /* Whether it is packed from the start or end */ - guint align : 1; /* Whether to align its position with adjacent rows */ - guint fixed : 1; /* Whether to require the same size for all rows */ -} CellInfo; - -typedef struct { - GList *cells; - - guint id : 8; - guint n_cells : 8; - guint expand_cells : 8; - guint align : 1; - guint visible : 1; -} CellGroup; - -typedef struct { - GtkCellRenderer *renderer; - - int position; - int size; -} AllocatedCell; - -static CellInfo *cell_info_new (GtkCellRenderer *renderer, - GtkPackType pack, - gboolean expand, - gboolean align, - gboolean fixed); -static void cell_info_free (CellInfo *info); -static int cell_info_find (CellInfo *info, - GtkCellRenderer *renderer); - -static AllocatedCell *allocated_cell_new (GtkCellRenderer *renderer, - int position, - int size); -static void allocated_cell_free (AllocatedCell *cell); -static GList *list_consecutive_cells (GtkCellAreaBox *box); -static int count_expand_groups (GtkCellAreaBox *box); -static void context_weak_notify (GtkCellAreaBox *box, - GtkCellAreaBoxContext *dead_context); -static void reset_contexts (GtkCellAreaBox *box); -static void init_context_groups (GtkCellAreaBox *box); -static void init_context_group (GtkCellAreaBox *box, - GtkCellAreaBoxContext *context); -static GSList *get_allocated_cells (GtkCellAreaBox *box, - GtkCellAreaBoxContext *context, - GtkWidget *widget, - int width, - int height); - -typedef struct _GtkCellAreaBoxClass GtkCellAreaBoxClass; -typedef struct _GtkCellAreaBoxPrivate GtkCellAreaBoxPrivate; - -struct _GtkCellAreaBox -{ - GtkCellArea parent_instance; -}; - -struct _GtkCellAreaBoxClass -{ - GtkCellAreaClass parent_class; -}; - -struct _GtkCellAreaBoxPrivate -{ - /* We hold on to the previously focused cell when navigating - * up and down in a horizontal box (or left and right on a vertical one) - * this way we always re-enter the last focused cell. - */ - GtkCellRenderer *last_focus_cell; - gulong focus_cell_id; - - GList *cells; - GArray *groups; - - GSList *contexts; - - GtkOrientation orientation; - int spacing; - - /* We hold on to the rtl state from a widget we are requested for - * so that we can navigate focus correctly - */ - gboolean rtl; -}; - -enum { - PROP_0, - PROP_ORIENTATION, - PROP_SPACING -}; - -enum { - CELL_PROP_0, - CELL_PROP_EXPAND, - CELL_PROP_ALIGN, - CELL_PROP_FIXED_SIZE, - CELL_PROP_PACK_TYPE -}; - -G_DEFINE_TYPE_WITH_CODE (GtkCellAreaBox, gtk_cell_area_box, GTK_TYPE_CELL_AREA, - G_ADD_PRIVATE (GtkCellAreaBox) - G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT, - gtk_cell_area_box_cell_layout_init) - G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL)) - -static void -gtk_cell_area_box_init (GtkCellAreaBox *box) -{ - GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); - - priv->orientation = GTK_ORIENTATION_HORIZONTAL; - priv->groups = g_array_new (FALSE, TRUE, sizeof (CellGroup)); - priv->cells = NULL; - priv->contexts = NULL; - priv->spacing = 0; - priv->rtl = FALSE; - - /* Watch whenever focus is given to a cell, even if it's not with keynav, - * this way we remember upon entry of the area where focus was last time - * around - */ - priv->focus_cell_id = g_signal_connect (box, "notify::focus-cell", - G_CALLBACK (gtk_cell_area_box_focus_changed), box); -} - -static void -gtk_cell_area_box_class_init (GtkCellAreaBoxClass *class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (class); - GtkCellAreaClass *area_class = GTK_CELL_AREA_CLASS (class); - - /* GObjectClass */ - object_class->finalize = gtk_cell_area_box_finalize; - object_class->dispose = gtk_cell_area_box_dispose; - object_class->set_property = gtk_cell_area_box_set_property; - object_class->get_property = gtk_cell_area_box_get_property; - - /* GtkCellAreaClass */ - area_class->add = gtk_cell_area_box_add; - area_class->remove = gtk_cell_area_box_remove; - area_class->foreach = gtk_cell_area_box_foreach; - area_class->foreach_alloc = gtk_cell_area_box_foreach_alloc; - area_class->apply_attributes = gtk_cell_area_box_apply_attributes; - area_class->set_cell_property = gtk_cell_area_box_set_cell_property; - area_class->get_cell_property = gtk_cell_area_box_get_cell_property; - - area_class->create_context = gtk_cell_area_box_create_context; - area_class->copy_context = gtk_cell_area_box_copy_context; - area_class->get_request_mode = gtk_cell_area_box_get_request_mode; - area_class->get_preferred_width = gtk_cell_area_box_get_preferred_width; - area_class->get_preferred_height = gtk_cell_area_box_get_preferred_height; - area_class->get_preferred_height_for_width = gtk_cell_area_box_get_preferred_height_for_width; - area_class->get_preferred_width_for_height = gtk_cell_area_box_get_preferred_width_for_height; - - area_class->focus = gtk_cell_area_box_focus; - - /* Properties */ - g_object_class_override_property (object_class, PROP_ORIENTATION, "orientation"); - - /** - * GtkCellAreaBox:spacing: - * - * The amount of space to reserve between cells. - */ - g_object_class_install_property (object_class, - PROP_SPACING, - g_param_spec_int ("spacing", NULL, NULL, - 0, - G_MAXINT, - 0, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - /* Cell Properties */ - /** - * GtkCellAreaBox:expand: - * - * Whether the cell renderer should receive extra space - * when the area receives more than its natural size. - */ - gtk_cell_area_class_install_cell_property (area_class, - CELL_PROP_EXPAND, - g_param_spec_boolean - ("expand", NULL, NULL, - FALSE, - GTK_PARAM_READWRITE)); - - /** - * GtkCellAreaBox:align: - * - * Whether the cell renderer should be aligned in adjacent rows. - */ - gtk_cell_area_class_install_cell_property (area_class, - CELL_PROP_ALIGN, - g_param_spec_boolean - ("align", NULL, NULL, - FALSE, - GTK_PARAM_READWRITE)); - - /** - * GtkCellAreaBox:fixed-size: - * - * Whether the cell renderer should require the same size - * for all rows for which it was requested. - */ - gtk_cell_area_class_install_cell_property (area_class, - CELL_PROP_FIXED_SIZE, - g_param_spec_boolean - ("fixed-size", NULL, NULL, - TRUE, - GTK_PARAM_READWRITE)); - - /** - * GtkCellAreaBox:pack-type: - * - * A GtkPackType indicating whether the cell renderer is packed - * with reference to the start or end of the area. - */ - gtk_cell_area_class_install_cell_property (area_class, - CELL_PROP_PACK_TYPE, - g_param_spec_enum - ("pack-type", NULL, NULL, - GTK_TYPE_PACK_TYPE, GTK_PACK_START, - GTK_PARAM_READWRITE)); -} - - -/************************************************************* - * CellInfo/CellGroup basics and convenience functions * - *************************************************************/ -static CellInfo * -cell_info_new (GtkCellRenderer *renderer, - GtkPackType pack, - gboolean expand, - gboolean align, - gboolean fixed) -{ - CellInfo *info = g_slice_new (CellInfo); - - info->renderer = g_object_ref_sink (renderer); - info->pack = pack; - info->expand = expand; - info->align = align; - info->fixed = fixed; - - return info; -} - -static void -cell_info_free (CellInfo *info) -{ - g_object_unref (info->renderer); - - g_slice_free (CellInfo, info); -} - -static int -cell_info_find (CellInfo *info, - GtkCellRenderer *renderer) -{ - return (info->renderer == renderer) ? 0 : -1; -} - -static AllocatedCell * -allocated_cell_new (GtkCellRenderer *renderer, - int position, - int size) -{ - AllocatedCell *cell = g_slice_new (AllocatedCell); - - cell->renderer = renderer; - cell->position = position; - cell->size = size; - - return cell; -} - -static void -allocated_cell_free (AllocatedCell *cell) -{ - g_slice_free (AllocatedCell, cell); -} - -static GList * -list_consecutive_cells (GtkCellAreaBox *box) -{ - GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); - GList *l, *consecutive_cells = NULL, *pack_end_cells = NULL; - CellInfo *info; - - /* List cells in consecutive order taking their - * PACK_START/PACK_END options into account - */ - for (l = priv->cells; l; l = l->next) - { - info = l->data; - - if (info->pack == GTK_PACK_START) - consecutive_cells = g_list_prepend (consecutive_cells, info); - } - - for (l = priv->cells; l; l = l->next) - { - info = l->data; - - if (info->pack == GTK_PACK_END) - pack_end_cells = g_list_prepend (pack_end_cells, info); - } - - consecutive_cells = g_list_reverse (consecutive_cells); - consecutive_cells = g_list_concat (consecutive_cells, pack_end_cells); - - return consecutive_cells; -} - -static void -cell_groups_clear (GtkCellAreaBox *box) -{ - GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); - int i; - - for (i = 0; i < priv->groups->len; i++) - { - CellGroup *group = &g_array_index (priv->groups, CellGroup, i); - - g_list_free (group->cells); - } - - g_array_set_size (priv->groups, 0); -} - -static void -cell_groups_rebuild (GtkCellAreaBox *box) -{ - GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); - CellGroup group = { 0, }; - CellGroup *group_ptr; - GList *cells, *l; - guint id = 0; - gboolean last_cell_fixed = FALSE; - - cell_groups_clear (box); - - if (!priv->cells) - return; - - cells = list_consecutive_cells (box); - - /* First group is implied */ - g_array_append_val (priv->groups, group); - group_ptr = &g_array_index (priv->groups, CellGroup, id); - - for (l = cells; l; l = l->next) - { - CellInfo *info = l->data; - - /* A new group starts with any aligned cell, or - * at the beginning and end of a fixed size cell. - * the first group is implied */ - if ((info->align || info->fixed || last_cell_fixed) && l != cells) - { - memset (&group, 0x0, sizeof (CellGroup)); - group.id = ++id; - - g_array_append_val (priv->groups, group); - group_ptr = &g_array_index (priv->groups, CellGroup, id); - } - - group_ptr->cells = g_list_prepend (group_ptr->cells, info); - group_ptr->n_cells++; - - /* Not every group is aligned, some are floating - * fixed size cells */ - if (info->align) - group_ptr->align = TRUE; - - /* A group expands if it contains any expand cells */ - if (info->expand) - group_ptr->expand_cells++; - - last_cell_fixed = info->fixed; - } - - g_list_free (cells); - - for (id = 0; id < priv->groups->len; id++) - { - group_ptr = &g_array_index (priv->groups, CellGroup, id); - - group_ptr->cells = g_list_reverse (group_ptr->cells); - } - - /* Contexts need to be updated with the new grouping information */ - init_context_groups (box); -} - -static int -count_visible_cells (CellGroup *group, - int *expand_cells) -{ - GList *l; - int visible_cells = 0; - int n_expand_cells = 0; - - for (l = group->cells; l; l = l->next) - { - CellInfo *info = l->data; - - if (gtk_cell_renderer_get_visible (info->renderer)) - { - visible_cells++; - - if (info->expand) - n_expand_cells++; - } - } - - if (expand_cells) - *expand_cells = n_expand_cells; - - return visible_cells; -} - -static int -count_expand_groups (GtkCellAreaBox *box) -{ - GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); - int i; - int expand_groups = 0; - - for (i = 0; i < priv->groups->len; i++) - { - CellGroup *group = &g_array_index (priv->groups, CellGroup, i); - - if (group->expand_cells > 0) - expand_groups++; - } - - return expand_groups; -} - -static void -context_weak_notify (GtkCellAreaBox *box, - GtkCellAreaBoxContext *dead_context) -{ - GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); - - priv->contexts = g_slist_remove (priv->contexts, dead_context); -} - -static void -init_context_group (GtkCellAreaBox *box, - GtkCellAreaBoxContext *context) -{ - GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); - int *expand_groups, *align_groups, i; - - expand_groups = g_new (gboolean, priv->groups->len); - align_groups = g_new (gboolean, priv->groups->len); - - for (i = 0; i < priv->groups->len; i++) - { - CellGroup *group = &g_array_index (priv->groups, CellGroup, i); - - expand_groups[i] = (group->expand_cells > 0); - align_groups[i] = group->align; - } - - /* This call implies resetting the request info */ - _gtk_cell_area_box_init_groups (context, priv->groups->len, expand_groups, align_groups); - g_free (expand_groups); - g_free (align_groups); -} - -static void -init_context_groups (GtkCellAreaBox *box) -{ - GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); - GSList *l; - - /* When the box's groups are reconstructed, - * contexts need to be reinitialized. - */ - for (l = priv->contexts; l; l = l->next) - { - GtkCellAreaBoxContext *context = l->data; - - init_context_group (box, context); - } -} - -static void -reset_contexts (GtkCellAreaBox *box) -{ - GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); - GSList *l; - - /* When the box layout changes, contexts need to - * be reset and sizes for the box get requested again - */ - for (l = priv->contexts; l; l = l->next) - { - GtkCellAreaContext *context = l->data; - - gtk_cell_area_context_reset (context); - } -} - -/* Fall back on a completely unaligned dynamic allocation of cells - * when not allocated for the said orientation, alignment of cells - * is not done when each area gets a different size in the orientation - * of the box. - */ -static GSList * -allocate_cells_manually (GtkCellAreaBox *box, - GtkWidget *widget, - int width, - int height) -{ - GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); - GList *cells, *l; - GSList *allocated_cells = NULL; - GtkRequestedSize *sizes; - int i; - int nvisible = 0, nexpand = 0, group_expand; - int avail_size, extra_size, extra_extra, full_size; - int position = 0, for_size; - gboolean rtl; - - if (!priv->cells) - return NULL; - - /* For vertical oriented boxes, we just let the cell renderers - * realign themselves for rtl - */ - rtl = (priv->orientation == GTK_ORIENTATION_HORIZONTAL && - gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL); - - cells = list_consecutive_cells (box); - - /* Count the visible and expand cells */ - for (i = 0; i < priv->groups->len; i++) - { - CellGroup *group = &g_array_index (priv->groups, CellGroup, i); - - nvisible += count_visible_cells (group, &group_expand); - nexpand += group_expand; - } - - if (nvisible <= 0) - { - g_list_free (cells); - return NULL; - } - - if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) - { - full_size = avail_size = width; - for_size = height; - } - else - { - full_size = avail_size = height; - for_size = width; - } - - /* Go ahead and collect the requests on the fly */ - sizes = g_new0 (GtkRequestedSize, nvisible); - for (l = cells, i = 0; l; l = l->next) - { - CellInfo *info = l->data; - - if (!gtk_cell_renderer_get_visible (info->renderer)) - continue; - - gtk_cell_area_request_renderer (GTK_CELL_AREA (box), info->renderer, - priv->orientation, - widget, for_size, - &sizes[i].minimum_size, - &sizes[i].natural_size); - - avail_size -= sizes[i].minimum_size; - - sizes[i].data = info; - - i++; - } - - /* Naturally distribute the allocation */ - avail_size -= (nvisible - 1) * priv->spacing; - if (avail_size > 0) - avail_size = gtk_distribute_natural_allocation (avail_size, nvisible, sizes); - else - avail_size = 0; - - /* Calculate/distribute expand for cells */ - if (nexpand > 0) - { - extra_size = avail_size / nexpand; - extra_extra = avail_size % nexpand; - } - else - extra_size = extra_extra = 0; - - /* Create the allocated cells */ - for (i = 0; i < nvisible; i++) - { - CellInfo *info = sizes[i].data; - AllocatedCell *cell; - - if (info->expand) - { - sizes[i].minimum_size += extra_size; - if (extra_extra) - { - sizes[i].minimum_size++; - extra_extra--; - } - } - - if (rtl) - cell = allocated_cell_new (info->renderer, - full_size - (position + sizes[i].minimum_size), - sizes[i].minimum_size); - else - cell = allocated_cell_new (info->renderer, position, sizes[i].minimum_size); - - allocated_cells = g_slist_prepend (allocated_cells, cell); - - position += sizes[i].minimum_size; - position += priv->spacing; - } - - g_free (sizes); - g_list_free (cells); - - /* Note it might not be important to reverse the list here at all, - * we have the correct positions, no need to allocate from left to right - */ - return g_slist_reverse (allocated_cells); -} - -/* Returns an allocation for each cell in the orientation of the box, - * used in ->render()/->event() implementations to get a straight-forward - * list of allocated cells to operate on. - */ -static GSList * -get_allocated_cells (GtkCellAreaBox *box, - GtkCellAreaBoxContext *context, - GtkWidget *widget, - int width, - int height) -{ - GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); - GtkCellAreaBoxAllocation *group_allocs; - GtkCellArea *area = GTK_CELL_AREA (box); - GList *cell_list; - GSList *allocated_cells = NULL; - int i, j, n_allocs, position; - int for_size, full_size; - gboolean rtl; - - group_allocs = _gtk_cell_area_box_context_get_orientation_allocs (context, &n_allocs); - if (!group_allocs) - return allocate_cells_manually (box, widget, width, height); - - if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) - { - full_size = width; - for_size = height; - } - else - { - full_size = height; - for_size = width; - } - - /* For vertical oriented boxes, we just let the cell renderers - * realign themselves for rtl - */ - rtl = (priv->orientation == GTK_ORIENTATION_HORIZONTAL && - gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL); - - for (position = 0, i = 0; i < n_allocs; i++) - { - /* We dont always allocate all groups, sometimes the requested - * group has only invisible cells for every row, hence the usage - * of group_allocs[i].group_idx here - */ - CellGroup *group = &g_array_index (priv->groups, CellGroup, group_allocs[i].group_idx); - - /* Exception for single cell groups */ - if (group->n_cells == 1) - { - CellInfo *info = group->cells->data; - AllocatedCell *cell; - int cell_position, cell_size; - - if (!gtk_cell_renderer_get_visible (info->renderer)) - continue; - - /* If were not aligned, place the cell after the last cell */ - if (info->align) - position = cell_position = group_allocs[i].position; - else - cell_position = position; - - /* If not a fixed size, use only the requested size for this row */ - if (info->fixed) - cell_size = group_allocs[i].size; - else - { - int dummy; - gtk_cell_area_request_renderer (area, info->renderer, - priv->orientation, - widget, for_size, - &dummy, - &cell_size); - cell_size = MIN (cell_size, group_allocs[i].size); - } - - if (rtl) - cell = allocated_cell_new (info->renderer, - full_size - (cell_position + cell_size), cell_size); - else - cell = allocated_cell_new (info->renderer, cell_position, cell_size); - - position += cell_size; - position += priv->spacing; - - allocated_cells = g_slist_prepend (allocated_cells, cell); - } - else - { - GtkRequestedSize *sizes; - int avail_size, cell_position; - int visible_cells, expand_cells; - int extra_size, extra_extra; - - visible_cells = count_visible_cells (group, &expand_cells); - - /* If this row has no visible cells in this group, just - * skip the allocation - */ - if (visible_cells == 0) - continue; - - /* If were not aligned, place the cell after the last cell - * and eat up the extra space - */ - if (group->align) - { - avail_size = group_allocs[i].size; - position = cell_position = group_allocs[i].position; - } - else - { - avail_size = group_allocs[i].size + (group_allocs[i].position - position); - cell_position = position; - } - - sizes = g_new (GtkRequestedSize, visible_cells); - - for (j = 0, cell_list = group->cells; cell_list; cell_list = cell_list->next) - { - CellInfo *info = cell_list->data; - - if (!gtk_cell_renderer_get_visible (info->renderer)) - continue; - - gtk_cell_area_request_renderer (area, info->renderer, - priv->orientation, - widget, for_size, - &sizes[j].minimum_size, - &sizes[j].natural_size); - - sizes[j].data = info; - avail_size -= sizes[j].minimum_size; - - j++; - } - - /* Distribute cells naturally within the group */ - avail_size -= (visible_cells - 1) * priv->spacing; - if (avail_size > 0) - avail_size = gtk_distribute_natural_allocation (avail_size, visible_cells, sizes); - else - avail_size = 0; - - /* Calculate/distribute expand for cells */ - if (expand_cells > 0) - { - extra_size = avail_size / expand_cells; - extra_extra = avail_size % expand_cells; - } - else - extra_size = extra_extra = 0; - - /* Create the allocated cells (loop only over visible cells here) */ - for (j = 0; j < visible_cells; j++) - { - CellInfo *info = sizes[j].data; - AllocatedCell *cell; - - if (info->expand) - { - sizes[j].minimum_size += extra_size; - if (extra_extra) - { - sizes[j].minimum_size++; - extra_extra--; - } - } - - if (rtl) - cell = allocated_cell_new (info->renderer, - full_size - (cell_position + sizes[j].minimum_size), - sizes[j].minimum_size); - else - cell = allocated_cell_new (info->renderer, cell_position, sizes[j].minimum_size); - - allocated_cells = g_slist_prepend (allocated_cells, cell); - - cell_position += sizes[j].minimum_size; - cell_position += priv->spacing; - } - - g_free (sizes); - - position = cell_position; - } - } - - g_free (group_allocs); - - /* Note it might not be important to reverse the list here at all, - * we have the correct positions, no need to allocate from left to right - */ - return g_slist_reverse (allocated_cells); -} - - -static void -gtk_cell_area_box_focus_changed (GtkCellArea *area, - GParamSpec *pspec, - GtkCellAreaBox *box) -{ - GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); - - if (gtk_cell_area_get_focus_cell (area)) - priv->last_focus_cell = gtk_cell_area_get_focus_cell (area); -} - -/************************************************************* - * GObjectClass * - *************************************************************/ -static void -gtk_cell_area_box_finalize (GObject *object) -{ - GtkCellAreaBox *box = GTK_CELL_AREA_BOX (object); - GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); - GSList *l; - - /* Unref/free the context list */ - for (l = priv->contexts; l; l = l->next) - g_object_weak_unref (G_OBJECT (l->data), (GWeakNotify)context_weak_notify, box); - - g_slist_free (priv->contexts); - priv->contexts = NULL; - - /* Free the cell grouping info */ - cell_groups_clear (box); - g_array_free (priv->groups, TRUE); - - G_OBJECT_CLASS (gtk_cell_area_box_parent_class)->finalize (object); -} - -static void -gtk_cell_area_box_dispose (GObject *object) -{ - G_OBJECT_CLASS (gtk_cell_area_box_parent_class)->dispose (object); -} - -static void -gtk_cell_area_box_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - GtkCellAreaBox *box = GTK_CELL_AREA_BOX (object); - GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); - - switch (prop_id) - { - case PROP_ORIENTATION: - if (priv->orientation != g_value_get_enum (value)) - { - priv->orientation = g_value_get_enum (value); - /* Notify that size needs to be requested again */ - reset_contexts (box); - g_object_notify_by_pspec (object, pspec); - } - break; - case PROP_SPACING: - gtk_cell_area_box_set_spacing (box, g_value_get_int (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gtk_cell_area_box_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GtkCellAreaBox *box = GTK_CELL_AREA_BOX (object); - GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); - - switch (prop_id) - { - case PROP_ORIENTATION: - g_value_set_enum (value, priv->orientation); - break; - case PROP_SPACING: - g_value_set_int (value, gtk_cell_area_box_get_spacing (box)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -/************************************************************* - * GtkCellAreaClass * - *************************************************************/ -static void -gtk_cell_area_box_add (GtkCellArea *area, - GtkCellRenderer *renderer) -{ - gtk_cell_area_box_pack_start (GTK_CELL_AREA_BOX (area), - renderer, FALSE, FALSE, TRUE); -} - -static void -gtk_cell_area_box_remove (GtkCellArea *area, - GtkCellRenderer *renderer) -{ - GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area); - GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); - GList *node; - - if (priv->last_focus_cell == renderer) - priv->last_focus_cell = NULL; - - node = g_list_find_custom (priv->cells, renderer, - (GCompareFunc)cell_info_find); - - if (node) - { - CellInfo *info = node->data; - - cell_info_free (info); - - priv->cells = g_list_delete_link (priv->cells, node); - - /* Reconstruct cell groups */ - cell_groups_rebuild (box); - } - else - g_warning ("Trying to remove a cell renderer that is not present GtkCellAreaBox"); -} - -static void -gtk_cell_area_box_foreach (GtkCellArea *area, - GtkCellCallback callback, - gpointer callback_data) -{ - GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area); - GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); - GList *list; - - for (list = priv->cells; list; list = list->next) - { - CellInfo *info = list->data; - - if (callback (info->renderer, callback_data)) - break; - } -} - -static void -gtk_cell_area_box_foreach_alloc (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - const GdkRectangle *cell_area, - const GdkRectangle *background_area, - GtkCellAllocCallback callback, - gpointer callback_data) -{ - GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area); - GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); - GtkCellAreaBoxContext *box_context = GTK_CELL_AREA_BOX_CONTEXT (context); - GSList *allocated_cells, *l; - GdkRectangle cell_alloc, cell_background; - gboolean rtl; - - rtl = (priv->orientation == GTK_ORIENTATION_HORIZONTAL && - gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL); - - cell_alloc = *cell_area; - - /* Get a list of cells with allocation sizes decided regardless - * of alignments and pack order etc. - */ - allocated_cells = get_allocated_cells (box, box_context, widget, - cell_area->width, cell_area->height); - - for (l = allocated_cells; l; l = l->next) - { - AllocatedCell *cell = l->data; - - if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) - { - cell_alloc.x = cell_area->x + cell->position; - cell_alloc.width = cell->size; - } - else - { - cell_alloc.y = cell_area->y + cell->position; - cell_alloc.height = cell->size; - } - - /* Stop iterating over cells if they flow out of the render - * area, this can happen because the render area can actually - * be smaller than the requested area (treeview columns can - * be user resizable and can be resized to be smaller than - * the actual requested area). - */ - if (cell_alloc.x > cell_area->x + cell_area->width || - cell_alloc.x + cell_alloc.width < cell_area->x || - cell_alloc.y > cell_area->y + cell_area->height) - break; - - /* Special case for the last cell (or first cell in rtl)... - * let the last cell consume the remaining space in the area - * (the last cell is allowed to consume the remaining space if - * the space given for rendering is actually larger than allocation, - * this can happen in the expander GtkTreeViewColumn where only the - * deepest depth column receives the allocation... shallow columns - * receive more width). */ - if (!l->next) - { - if (rtl) - { - /* Fill the leading space for the first cell in the area - * (still last in the list) - */ - cell_alloc.width = (cell_alloc.x - cell_area->x) + cell_alloc.width; - cell_alloc.x = cell_area->x; - } - else - { - cell_alloc.width = cell_area->x + cell_area->width - cell_alloc.x; - cell_alloc.height = cell_area->y + cell_area->height - cell_alloc.y; - } - } - else - { - /* If the cell we are rendering doesn't fit into the remaining space, - * clip it so that the underlying renderer has a chance to deal with - * it (for instance text renderers get a chance to ellipsize). - */ - if (cell_alloc.x + cell_alloc.width > cell_area->x + cell_area->width) - cell_alloc.width = cell_area->x + cell_area->width - cell_alloc.x; - - if (cell_alloc.y + cell_alloc.height > cell_area->y + cell_area->height) - cell_alloc.height = cell_area->y + cell_area->height - cell_alloc.y; - } - - /* Add portions of the background_area to the cell_alloc - * to create the cell_background - */ - cell_background = cell_alloc; - - if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) - { - if (l == allocated_cells) - { - /* Add the depth to the first cell */ - if (rtl) - { - cell_background.width += background_area->width - cell_area->width; - cell_background.x = background_area->x + background_area->width - cell_background.width; - } - else - { - cell_background.width += cell_area->x - background_area->x; - cell_background.x = background_area->x; - } - } - - if (l->next == NULL) - { - /* Grant this cell the remaining space */ - int remain = cell_background.x - background_area->x; - - if (rtl) - cell_background.x -= remain; - else - cell_background.width = background_area->width - remain; - } - - cell_background.y = background_area->y; - cell_background.height = background_area->height; - } - else - { - if (l == allocated_cells) - { - cell_background.height += cell_background.y - background_area->y; - cell_background.y = background_area->y; - } - - if (l->next == NULL) - cell_background.height = - background_area->height - (cell_background.y - background_area->y); - - cell_background.x = background_area->x; - cell_background.width = background_area->width; - } - - if (callback (cell->renderer, &cell_alloc, &cell_background, callback_data)) - break; - } - - g_slist_free_full (allocated_cells, (GDestroyNotify)allocated_cell_free); -} - -static void -gtk_cell_area_box_apply_attributes (GtkCellArea *area, - GtkTreeModel *tree_model, - GtkTreeIter *iter, - gboolean is_expander, - gboolean is_expanded) -{ - GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area); - GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); - int i; - - /* Call the parent class to apply the attributes */ - GTK_CELL_AREA_CLASS - (gtk_cell_area_box_parent_class)->apply_attributes (area, tree_model, iter, - is_expander, is_expanded); - - /* Update visible state for cell groups */ - for (i = 0; i < priv->groups->len; i++) - { - CellGroup *group = &g_array_index (priv->groups, CellGroup, i); - GList *list; - - group->visible = FALSE; - - for (list = group->cells; list && group->visible == FALSE; list = list->next) - { - CellInfo *info = list->data; - - if (gtk_cell_renderer_get_visible (info->renderer)) - group->visible = TRUE; - } - } -} - -static void -gtk_cell_area_box_set_cell_property (GtkCellArea *area, - GtkCellRenderer *renderer, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area); - GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); - GList *node; - CellInfo *info; - gboolean rebuild = FALSE; - gboolean val; - GtkPackType pack_type; - - node = g_list_find_custom (priv->cells, renderer, - (GCompareFunc)cell_info_find); - if (!node) - return; - - info = node->data; - - switch (prop_id) - { - case CELL_PROP_EXPAND: - val = g_value_get_boolean (value); - - if (info->expand != val) - { - info->expand = val; - rebuild = TRUE; - } - break; - - case CELL_PROP_ALIGN: - val = g_value_get_boolean (value); - - if (info->align != val) - { - info->align = val; - rebuild = TRUE; - } - break; - - case CELL_PROP_FIXED_SIZE: - val = g_value_get_boolean (value); - - if (info->fixed != val) - { - info->fixed = val; - rebuild = TRUE; - } - break; - - case CELL_PROP_PACK_TYPE: - pack_type = g_value_get_enum (value); - - if (info->pack != pack_type) - { - info->pack = pack_type; - rebuild = TRUE; - } - break; - default: - GTK_CELL_AREA_WARN_INVALID_CELL_PROPERTY_ID (area, prop_id, pspec); - break; - } - - /* Groups need to be rebuilt */ - if (rebuild) - cell_groups_rebuild (box); -} - -static void -gtk_cell_area_box_get_cell_property (GtkCellArea *area, - GtkCellRenderer *renderer, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area); - GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); - GList *node; - CellInfo *info; - - node = g_list_find_custom (priv->cells, renderer, - (GCompareFunc)cell_info_find); - if (!node) - return; - - info = node->data; - - switch (prop_id) - { - case CELL_PROP_EXPAND: - g_value_set_boolean (value, info->expand); - break; - - case CELL_PROP_ALIGN: - g_value_set_boolean (value, info->align); - break; - - case CELL_PROP_FIXED_SIZE: - g_value_set_boolean (value, info->fixed); - break; - - case CELL_PROP_PACK_TYPE: - g_value_set_enum (value, info->pack); - break; - default: - GTK_CELL_AREA_WARN_INVALID_CELL_PROPERTY_ID (area, prop_id, pspec); - break; - } -} - - -static GtkCellAreaContext * -gtk_cell_area_box_create_context (GtkCellArea *area) -{ - GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area); - GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); - GtkCellAreaContext *context = - (GtkCellAreaContext *)g_object_new (GTK_TYPE_CELL_AREA_BOX_CONTEXT, - "area", area, NULL); - - priv->contexts = g_slist_prepend (priv->contexts, context); - - g_object_weak_ref (G_OBJECT (context), (GWeakNotify)context_weak_notify, box); - - /* Tell the new group about our cell layout */ - init_context_group (box, GTK_CELL_AREA_BOX_CONTEXT (context)); - - return context; -} - -static GtkCellAreaContext * -gtk_cell_area_box_copy_context (GtkCellArea *area, - GtkCellAreaContext *context) -{ - GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area); - GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); - GtkCellAreaContext *copy = - (GtkCellAreaContext *)_gtk_cell_area_box_context_copy (GTK_CELL_AREA_BOX (area), - GTK_CELL_AREA_BOX_CONTEXT (context)); - - priv->contexts = g_slist_prepend (priv->contexts, copy); - - g_object_weak_ref (G_OBJECT (copy), (GWeakNotify)context_weak_notify, box); - - return copy; -} - -static GtkSizeRequestMode -gtk_cell_area_box_get_request_mode (GtkCellArea *area) -{ - GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area); - GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); - - return (priv->orientation) == GTK_ORIENTATION_HORIZONTAL ? - GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH : - GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT; -} - -static void -compute_size (GtkCellAreaBox *box, - GtkOrientation orientation, - GtkCellAreaBoxContext *context, - GtkWidget *widget, - int for_size, - int *minimum_size, - int *natural_size) -{ - GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); - GtkCellArea *area = GTK_CELL_AREA (box); - GList *list; - int i; - int min_size = 0; - int nat_size = 0; - - for (i = 0; i < priv->groups->len; i++) - { - CellGroup *group = &g_array_index (priv->groups, CellGroup, i); - int group_min_size = 0; - int group_nat_size = 0; - - for (list = group->cells; list; list = list->next) - { - CellInfo *info = list->data; - int renderer_min_size, renderer_nat_size; - - if (!gtk_cell_renderer_get_visible (info->renderer)) - continue; - - gtk_cell_area_request_renderer (area, info->renderer, orientation, widget, for_size, - &renderer_min_size, &renderer_nat_size); - - if (orientation == priv->orientation) - { - if (min_size > 0) - { - min_size += priv->spacing; - nat_size += priv->spacing; - } - - if (group_min_size > 0) - { - group_min_size += priv->spacing; - group_nat_size += priv->spacing; - } - - min_size += renderer_min_size; - nat_size += renderer_nat_size; - group_min_size += renderer_min_size; - group_nat_size += renderer_nat_size; - } - else - { - min_size = MAX (min_size, renderer_min_size); - nat_size = MAX (nat_size, renderer_nat_size); - group_min_size = MAX (group_min_size, renderer_min_size); - group_nat_size = MAX (group_nat_size, renderer_nat_size); - } - } - - if (orientation == GTK_ORIENTATION_HORIZONTAL) - { - if (for_size < 0) - _gtk_cell_area_box_context_push_group_width (context, group->id, group_min_size, group_nat_size); - else - _gtk_cell_area_box_context_push_group_width_for_height (context, group->id, for_size, - group_min_size, group_nat_size); - } - else - { - if (for_size < 0) - _gtk_cell_area_box_context_push_group_height (context, group->id, group_min_size, group_nat_size); - else - _gtk_cell_area_box_context_push_group_height_for_width (context, group->id, for_size, - group_min_size, group_nat_size); - } - } - - *minimum_size = min_size; - *natural_size = nat_size; - - /* Update rtl state for focus navigation to work */ - priv->rtl = (priv->orientation == GTK_ORIENTATION_HORIZONTAL && - gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL); -} - -static GtkRequestedSize * -get_group_sizes (GtkCellArea *area, - CellGroup *group, - GtkOrientation orientation, - GtkWidget *widget, - int *n_sizes) -{ - GtkRequestedSize *sizes; - GList *l; - int i; - - *n_sizes = count_visible_cells (group, NULL); - sizes = g_new (GtkRequestedSize, *n_sizes); - - for (l = group->cells, i = 0; l; l = l->next) - { - CellInfo *info = l->data; - - if (!gtk_cell_renderer_get_visible (info->renderer)) - continue; - - sizes[i].data = info; - - gtk_cell_area_request_renderer (area, info->renderer, - orientation, widget, -1, - &sizes[i].minimum_size, - &sizes[i].natural_size); - - i++; - } - - return sizes; -} - -static void -compute_group_size_for_opposing_orientation (GtkCellAreaBox *box, - CellGroup *group, - GtkWidget *widget, - int for_size, - int *minimum_size, - int *natural_size) -{ - GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); - GtkCellArea *area = GTK_CELL_AREA (box); - - /* Exception for single cell groups */ - if (group->n_cells == 1) - { - CellInfo *info = group->cells->data; - - gtk_cell_area_request_renderer (area, info->renderer, - OPPOSITE_ORIENTATION (priv->orientation), - widget, for_size, minimum_size, natural_size); - } - else - { - GtkRequestedSize *orientation_sizes; - CellInfo *info; - int n_sizes, i; - int avail_size = for_size; - int extra_size, extra_extra; - int min_size = 0, nat_size = 0; - - orientation_sizes = get_group_sizes (area, group, priv->orientation, widget, &n_sizes); - - /* First naturally allocate the cells in the group into the for_size */ - avail_size -= (n_sizes - 1) * priv->spacing; - for (i = 0; i < n_sizes; i++) - avail_size -= orientation_sizes[i].minimum_size; - - if (avail_size > 0) - avail_size = gtk_distribute_natural_allocation (avail_size, n_sizes, orientation_sizes); - else - avail_size = 0; - - /* Calculate/distribute expand for cells */ - if (group->expand_cells > 0) - { - extra_size = avail_size / group->expand_cells; - extra_extra = avail_size % group->expand_cells; - } - else - extra_size = extra_extra = 0; - - for (i = 0; i < n_sizes; i++) - { - int cell_min, cell_nat; - - info = orientation_sizes[i].data; - - if (info->expand) - { - orientation_sizes[i].minimum_size += extra_size; - if (extra_extra) - { - orientation_sizes[i].minimum_size++; - extra_extra--; - } - } - - gtk_cell_area_request_renderer (area, info->renderer, - OPPOSITE_ORIENTATION (priv->orientation), - widget, - orientation_sizes[i].minimum_size, - &cell_min, &cell_nat); - - min_size = MAX (min_size, cell_min); - nat_size = MAX (nat_size, cell_nat); - } - - *minimum_size = min_size; - *natural_size = nat_size; - - g_free (orientation_sizes); - } -} - -static void -compute_size_for_opposing_orientation (GtkCellAreaBox *box, - GtkCellAreaBoxContext *context, - GtkWidget *widget, - int for_size, - int *minimum_size, - int *natural_size) -{ - GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); - CellGroup *group; - GtkRequestedSize *orientation_sizes; - int n_groups, n_expand_groups, i; - int avail_size = for_size; - int extra_size, extra_extra; - int min_size = 0, nat_size = 0; - - n_expand_groups = count_expand_groups (box); - - if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) - orientation_sizes = _gtk_cell_area_box_context_get_widths (context, &n_groups); - else - orientation_sizes = _gtk_cell_area_box_context_get_heights (context, &n_groups); - - /* First start by naturally allocating space among groups of cells */ - avail_size -= (n_groups - 1) * priv->spacing; - for (i = 0; i < n_groups; i++) - avail_size -= orientation_sizes[i].minimum_size; - - if (avail_size > 0) - avail_size = gtk_distribute_natural_allocation (avail_size, n_groups, orientation_sizes); - else - avail_size = 0; - - /* Calculate/distribute expand for groups */ - if (n_expand_groups > 0) - { - extra_size = avail_size / n_expand_groups; - extra_extra = avail_size % n_expand_groups; - } - else - extra_size = extra_extra = 0; - - /* Now we need to naturally allocate sizes for cells in each group - * and push the height-for-width for each group accordingly while - * accumulating the overall height-for-width for this row. - */ - for (i = 0; i < n_groups; i++) - { - int group_min, group_nat; - int group_idx = GPOINTER_TO_INT (orientation_sizes[i].data); - - group = &g_array_index (priv->groups, CellGroup, group_idx); - - if (group->expand_cells > 0) - { - orientation_sizes[i].minimum_size += extra_size; - if (extra_extra) - { - orientation_sizes[i].minimum_size++; - extra_extra--; - } - } - - /* Now we have the allocation for the group, - * request its height-for-width - */ - compute_group_size_for_opposing_orientation (box, group, widget, - orientation_sizes[i].minimum_size, - &group_min, &group_nat); - - min_size = MAX (min_size, group_min); - nat_size = MAX (nat_size, group_nat); - - if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) - { - _gtk_cell_area_box_context_push_group_height_for_width (context, group_idx, for_size, - group_min, group_nat); - } - else - { - _gtk_cell_area_box_context_push_group_width_for_height (context, group_idx, for_size, - group_min, group_nat); - } - } - - *minimum_size = min_size; - *natural_size = nat_size; - - g_free (orientation_sizes); - - /* Update rtl state for focus navigation to work */ - priv->rtl = (priv->orientation == GTK_ORIENTATION_HORIZONTAL && - gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL); -} - - - -static void -gtk_cell_area_box_get_preferred_width (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - int *minimum_width, - int *natural_width) -{ - GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area); - GtkCellAreaBoxContext *box_context; - int min_width, nat_width; - - g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (context)); - - box_context = GTK_CELL_AREA_BOX_CONTEXT (context); - - /* Compute the size of all renderers for current row data, - * bumping cell alignments in the context along the way - */ - compute_size (box, GTK_ORIENTATION_HORIZONTAL, - box_context, widget, -1, &min_width, &nat_width); - - if (minimum_width) - *minimum_width = min_width; - - if (natural_width) - *natural_width = nat_width; -} - -static void -gtk_cell_area_box_get_preferred_height (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - int *minimum_height, - int *natural_height) -{ - GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area); - GtkCellAreaBoxContext *box_context; - int min_height, nat_height; - - g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (context)); - - box_context = GTK_CELL_AREA_BOX_CONTEXT (context); - - /* Compute the size of all renderers for current row data, - * bumping cell alignments in the context along the way - */ - compute_size (box, GTK_ORIENTATION_VERTICAL, - box_context, widget, -1, &min_height, &nat_height); - - if (minimum_height) - *minimum_height = min_height; - - if (natural_height) - *natural_height = nat_height; -} - -static void -gtk_cell_area_box_get_preferred_height_for_width (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - int width, - int *minimum_height, - int *natural_height) -{ - GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area); - GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); - GtkCellAreaBoxContext *box_context = GTK_CELL_AREA_BOX_CONTEXT (context); - int min_height, nat_height; - - g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (context)); - - if (priv->orientation == GTK_ORIENTATION_VERTICAL) - { - /* Add up vertical requests of height for width and push - * the overall cached sizes for alignments - */ - compute_size (box, priv->orientation, box_context, widget, width, &min_height, &nat_height); - } - else - { - /* Juice: virtually allocate cells into the for_width using the - * alignments and then return the overall height for that width, - * and cache it - */ - compute_size_for_opposing_orientation (box, box_context, widget, width, &min_height, &nat_height); - } - - if (minimum_height) - *minimum_height = min_height; - - if (natural_height) - *natural_height = nat_height; -} - -static void -gtk_cell_area_box_get_preferred_width_for_height (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - int height, - int *minimum_width, - int *natural_width) -{ - GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area); - GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); - GtkCellAreaBoxContext *box_context = GTK_CELL_AREA_BOX_CONTEXT (context); - int min_width, nat_width; - - g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (context)); - - if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) - { - /* Add up horizontal requests of width for height and push - * the overall cached sizes for alignments - */ - compute_size (box, priv->orientation, box_context, widget, height, &min_width, &nat_width); - } - else - { - /* Juice: horizontally allocate cells into the for_height using the - * alignments and then return the overall width for that height, - * and cache it - */ - compute_size_for_opposing_orientation (box, box_context, widget, height, &min_width, &nat_width); - } - - if (minimum_width) - *minimum_width = min_width; - - if (natural_width) - *natural_width = nat_width; -} - -enum { - FOCUS_NONE, - FOCUS_PREV, - FOCUS_NEXT, - FOCUS_LAST_CELL -}; - -static gboolean -gtk_cell_area_box_focus (GtkCellArea *area, - GtkDirectionType direction) -{ - GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area); - GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); - int cycle = FOCUS_NONE; - gboolean cycled_focus = FALSE; - GtkCellRenderer *focus_cell; - - focus_cell = gtk_cell_area_get_focus_cell (area); - - /* Special case, when there is no activatable cell, focus - * is painted around the entire area... in this case we - * let focus leave the area directly. - */ - if (focus_cell && !gtk_cell_area_is_activatable (area)) - { - gtk_cell_area_set_focus_cell (area, NULL); - return FALSE; - } - - switch (direction) - { - case GTK_DIR_TAB_FORWARD: - cycle = priv->rtl ? FOCUS_PREV : FOCUS_NEXT; - break; - case GTK_DIR_TAB_BACKWARD: - cycle = priv->rtl ? FOCUS_NEXT : FOCUS_PREV; - break; - case GTK_DIR_UP: - if (priv->orientation == GTK_ORIENTATION_VERTICAL || !priv->last_focus_cell) - cycle = FOCUS_PREV; - else if (!focus_cell) - cycle = FOCUS_LAST_CELL; - break; - case GTK_DIR_DOWN: - if (priv->orientation == GTK_ORIENTATION_VERTICAL || !priv->last_focus_cell) - cycle = FOCUS_NEXT; - else if (!focus_cell) - cycle = FOCUS_LAST_CELL; - break; - case GTK_DIR_LEFT: - if (priv->orientation == GTK_ORIENTATION_HORIZONTAL || !priv->last_focus_cell) - cycle = priv->rtl ? FOCUS_NEXT : FOCUS_PREV; - else if (!focus_cell) - cycle = FOCUS_LAST_CELL; - break; - case GTK_DIR_RIGHT: - if (priv->orientation == GTK_ORIENTATION_HORIZONTAL || !priv->last_focus_cell) - cycle = priv->rtl ? FOCUS_PREV : FOCUS_NEXT; - else if (!focus_cell) - cycle = FOCUS_LAST_CELL; - break; - default: - break; - } - - if (cycle == FOCUS_LAST_CELL) - { - gtk_cell_area_set_focus_cell (area, priv->last_focus_cell); - cycled_focus = TRUE; - } - else if (cycle != FOCUS_NONE) - { - gboolean found_cell = FALSE; - GList *list; - int i; - - /* If there is no focused cell, focus on the first (or last) one */ - if (!focus_cell) - found_cell = TRUE; - - for (i = (cycle == FOCUS_NEXT) ? 0 : priv->groups->len -1; - cycled_focus == FALSE && i >= 0 && i < priv->groups->len; - i = (cycle == FOCUS_NEXT) ? i + 1 : i - 1) - { - CellGroup *group = &g_array_index (priv->groups, CellGroup, i); - - for (list = (cycle == FOCUS_NEXT) ? g_list_first (group->cells) : g_list_last (group->cells); - cycled_focus == FALSE && list; list = (cycle == FOCUS_NEXT) ? list->next : list->prev) - { - CellInfo *info = list->data; - - if (info->renderer == focus_cell) - found_cell = TRUE; - else if (found_cell && /* Dont give focus to cells that are siblings to a focus cell */ - gtk_cell_area_get_focus_from_sibling (area, info->renderer) == NULL) - { - gtk_cell_area_set_focus_cell (area, info->renderer); - cycled_focus = TRUE; - } - } - } - } - - if (!cycled_focus) - gtk_cell_area_set_focus_cell (area, NULL); - - return cycled_focus; -} - - -/************************************************************* - * GtkCellLayoutIface * - *************************************************************/ -static void -gtk_cell_area_box_cell_layout_init (GtkCellLayoutIface *iface) -{ - iface->pack_start = gtk_cell_area_box_layout_pack_start; - iface->pack_end = gtk_cell_area_box_layout_pack_end; - iface->reorder = gtk_cell_area_box_layout_reorder; -} - -static void -gtk_cell_area_box_layout_pack_start (GtkCellLayout *cell_layout, - GtkCellRenderer *renderer, - gboolean expand) -{ - gtk_cell_area_box_pack_start (GTK_CELL_AREA_BOX (cell_layout), renderer, expand, FALSE, TRUE); -} - -static void -gtk_cell_area_box_layout_pack_end (GtkCellLayout *cell_layout, - GtkCellRenderer *renderer, - gboolean expand) -{ - gtk_cell_area_box_pack_end (GTK_CELL_AREA_BOX (cell_layout), renderer, expand, FALSE, TRUE); -} - -static void -gtk_cell_area_box_layout_reorder (GtkCellLayout *cell_layout, - GtkCellRenderer *renderer, - int position) -{ - GtkCellAreaBox *box = GTK_CELL_AREA_BOX (cell_layout); - GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); - GList *node; - CellInfo *info; - - node = g_list_find_custom (priv->cells, renderer, - (GCompareFunc)cell_info_find); - - if (node) - { - info = node->data; - - priv->cells = g_list_delete_link (priv->cells, node); - priv->cells = g_list_insert (priv->cells, info, position); - - cell_groups_rebuild (box); - } -} - -/************************************************************* - * Private interaction with GtkCellAreaBoxContext * - *************************************************************/ -gboolean -_gtk_cell_area_box_group_visible (GtkCellAreaBox *box, - int group_idx) -{ - GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); - CellGroup *group; - - g_assert (group_idx >= 0 && group_idx < priv->groups->len); - - group = &g_array_index (priv->groups, CellGroup, group_idx); - - return group->visible; -} - - -/************************************************************* - * API * - *************************************************************/ -/** - * gtk_cell_area_box_new: - * - * Creates a new `GtkCellAreaBox`. - * - * Returns: a newly created `GtkCellAreaBox` - */ -GtkCellArea * -gtk_cell_area_box_new (void) -{ - return (GtkCellArea *)g_object_new (GTK_TYPE_CELL_AREA_BOX, NULL); -} - -/** - * gtk_cell_area_box_pack_start: - * @box: a `GtkCellAreaBox` - * @renderer: the `GtkCellRenderer` to add - * @expand: whether @renderer should receive extra space when the area receives - * more than its natural size - * @align: whether @renderer should be aligned in adjacent rows - * @fixed: whether @renderer should have the same size in all rows - * - * Adds @renderer to @box, packed with reference to the start of @box. - * - * The @renderer is packed after any other `GtkCellRenderer` packed - * with reference to the start of @box. - */ -void -gtk_cell_area_box_pack_start (GtkCellAreaBox *box, - GtkCellRenderer *renderer, - gboolean expand, - gboolean align, - gboolean fixed) -{ - GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); - CellInfo *info; - - g_return_if_fail (GTK_IS_CELL_AREA_BOX (box)); - g_return_if_fail (GTK_IS_CELL_RENDERER (renderer)); - - if (g_list_find_custom (priv->cells, renderer, - (GCompareFunc)cell_info_find)) - { - g_warning ("Refusing to add the same cell renderer to a GtkCellAreaBox twice"); - return; - } - - info = cell_info_new (renderer, GTK_PACK_START, expand, align, fixed); - - priv->cells = g_list_append (priv->cells, info); - - cell_groups_rebuild (box); -} - -/** - * gtk_cell_area_box_pack_end: - * @box: a `GtkCellAreaBox` - * @renderer: the `GtkCellRenderer` to add - * @expand: whether @renderer should receive extra space when the area receives - * more than its natural size - * @align: whether @renderer should be aligned in adjacent rows - * @fixed: whether @renderer should have the same size in all rows - * - * Adds @renderer to @box, packed with reference to the end of @box. - * - * The @renderer is packed after (away from end of) any other - * `GtkCellRenderer` packed with reference to the end of @box. - */ -void -gtk_cell_area_box_pack_end (GtkCellAreaBox *box, - GtkCellRenderer *renderer, - gboolean expand, - gboolean align, - gboolean fixed) -{ - GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); - CellInfo *info; - - g_return_if_fail (GTK_IS_CELL_AREA_BOX (box)); - g_return_if_fail (GTK_IS_CELL_RENDERER (renderer)); - - if (g_list_find_custom (priv->cells, renderer, - (GCompareFunc)cell_info_find)) - { - g_warning ("Refusing to add the same cell renderer to a GtkCellArea twice"); - return; - } - - info = cell_info_new (renderer, GTK_PACK_END, expand, align, fixed); - - priv->cells = g_list_append (priv->cells, info); - - cell_groups_rebuild (box); -} - -/** - * gtk_cell_area_box_get_spacing: - * @box: a `GtkCellAreaBox` - * - * Gets the spacing added between cell renderers. - * - * Returns: the space added between cell renderers in @box. - */ -int -gtk_cell_area_box_get_spacing (GtkCellAreaBox *box) -{ - GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); - - g_return_val_if_fail (GTK_IS_CELL_AREA_BOX (box), 0); - - return priv->spacing; -} - -/** - * gtk_cell_area_box_set_spacing: - * @box: a `GtkCellAreaBox` - * @spacing: the space to add between `GtkCellRenderer`s - * - * Sets the spacing to add between cell renderers in @box. - */ -void -gtk_cell_area_box_set_spacing (GtkCellAreaBox *box, - int spacing) -{ - GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box); - - g_return_if_fail (GTK_IS_CELL_AREA_BOX (box)); - - if (priv->spacing != spacing) - { - priv->spacing = spacing; - - g_object_notify (G_OBJECT (box), "spacing"); - - /* Notify that size needs to be requested again */ - reset_contexts (box); - } -} diff --git a/gtk/gtkcellareabox.h b/gtk/gtkcellareabox.h deleted file mode 100644 index a74c016afa..0000000000 --- a/gtk/gtkcellareabox.h +++ /dev/null @@ -1,70 +0,0 @@ -/* gtkcellareabox.h - * - * Copyright (C) 2010 Openismus GmbH - * - * Authors: - * Tristan Van Berkom - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#ifndef __GTK_CELL_AREA_BOX_H__ -#define __GTK_CELL_AREA_BOX_H__ - -#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) -#error "Only can be included directly." -#endif - -#include - -G_BEGIN_DECLS - -#define GTK_TYPE_CELL_AREA_BOX (gtk_cell_area_box_get_type ()) -#define GTK_CELL_AREA_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_AREA_BOX, GtkCellAreaBox)) -#define GTK_IS_CELL_AREA_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_AREA_BOX)) - -typedef struct _GtkCellAreaBox GtkCellAreaBox; - -GDK_AVAILABLE_IN_ALL -GType gtk_cell_area_box_get_type (void) G_GNUC_CONST; - -GDK_AVAILABLE_IN_ALL -GtkCellArea *gtk_cell_area_box_new (void); -GDK_AVAILABLE_IN_ALL -void gtk_cell_area_box_pack_start (GtkCellAreaBox *box, - GtkCellRenderer *renderer, - gboolean expand, - gboolean align, - gboolean fixed); -GDK_AVAILABLE_IN_ALL -void gtk_cell_area_box_pack_end (GtkCellAreaBox *box, - GtkCellRenderer *renderer, - gboolean expand, - gboolean align, - gboolean fixed); -GDK_AVAILABLE_IN_ALL -int gtk_cell_area_box_get_spacing (GtkCellAreaBox *box); -GDK_AVAILABLE_IN_ALL -void gtk_cell_area_box_set_spacing (GtkCellAreaBox *box, - int spacing); - -/* Private interaction with GtkCellAreaBoxContext */ -gboolean _gtk_cell_area_box_group_visible (GtkCellAreaBox *box, - int group_idx); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkCellAreaBox, g_object_unref) - -G_END_DECLS - -#endif /* __GTK_CELL_AREA_BOX_H__ */ diff --git a/gtk/gtkcellareaboxcontext.c b/gtk/gtkcellareaboxcontext.c deleted file mode 100644 index f56f857c22..0000000000 --- a/gtk/gtkcellareaboxcontext.c +++ /dev/null @@ -1,858 +0,0 @@ -/* gtkcellareaboxcontext.c - * - * Copyright (C) 2010 Openismus GmbH - * - * Authors: - * Tristan Van Berkom - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#include "config.h" -#include "gtkcellareabox.h" -#include "gtkcellareaboxcontextprivate.h" -#include "gtkorientable.h" - -#include "gtkprivate.h" - -/* GObjectClass */ -static void _gtk_cell_area_box_context_finalize (GObject *object); - -/* GtkCellAreaContextClass */ -static void _gtk_cell_area_box_context_reset (GtkCellAreaContext *context); -static void _gtk_cell_area_box_context_get_preferred_height_for_width (GtkCellAreaContext *context, - int width, - int *minimum_height, - int *natural_height); -static void _gtk_cell_area_box_context_get_preferred_width_for_height (GtkCellAreaContext *context, - int height, - int *minimum_width, - int *natural_width); - - - -/* Internal functions */ -static void _gtk_cell_area_box_context_sum (GtkCellAreaBoxContext *context, - GtkOrientation orientation, - int for_size, - int *minimum_size, - int *natural_size); -static void free_cache_array (GArray *array); -static GArray *group_array_new (GtkCellAreaBoxContext *context); -static GArray *get_array (GtkCellAreaBoxContext *context, - GtkOrientation orientation, - int for_size); -static gboolean group_expands (GtkCellAreaBoxContext *context, - int group_idx); -static int count_expand_groups (GtkCellAreaBoxContext *context); - - -/* CachedSize management */ -typedef struct { - int min_size; - int nat_size; -} CachedSize; - -struct _GtkCellAreaBoxContextPrivate -{ - /* Table of per renderer CachedSizes */ - GArray *base_widths; - GArray *base_heights; - - /* Table of per height/width hash tables of per renderer CachedSizes */ - GHashTable *widths; - GHashTable *heights; - - /* Whether each group expands */ - gboolean *expand; - - /* Whether each group is aligned */ - gboolean *align; -}; - -G_DEFINE_TYPE_WITH_PRIVATE (GtkCellAreaBoxContext, _gtk_cell_area_box_context, GTK_TYPE_CELL_AREA_CONTEXT) - -static void -free_cache_array (GArray *array) -{ - g_array_free (array, TRUE); -} - -static GArray * -group_array_new (GtkCellAreaBoxContext *context) -{ - GtkCellAreaBoxContextPrivate *priv = context->priv; - GArray *group_array; - - group_array = g_array_new (FALSE, TRUE, sizeof (CachedSize)); - g_array_set_size (group_array, priv->base_widths->len); - - return group_array; -} - -static GArray * -get_array (GtkCellAreaBoxContext *context, - GtkOrientation orientation, - int for_size) -{ - GtkCellAreaBoxContextPrivate *priv = context->priv; - GArray *array; - - if (for_size < 0) - { - if (orientation == GTK_ORIENTATION_HORIZONTAL) - array = priv->base_widths; - else - array = priv->base_heights; - } - else - { - if (orientation == GTK_ORIENTATION_HORIZONTAL) - { - array = g_hash_table_lookup (priv->widths, GINT_TO_POINTER (for_size)); - - if (!array) - array = priv->base_widths; - } - else - { - array = g_hash_table_lookup (priv->heights, GINT_TO_POINTER (for_size)); - - if (!array) - array = priv->base_heights; - } - } - - return array; -} - -static gboolean -group_expands (GtkCellAreaBoxContext *context, - int group_idx) -{ - GtkCellAreaBoxContextPrivate *priv = context->priv; - - g_assert (group_idx >= 0 && group_idx < priv->base_widths->len); - - return priv->expand[group_idx]; -} - -static int -count_expand_groups (GtkCellAreaBoxContext *context) -{ - GtkCellAreaBoxContextPrivate *priv = context->priv; - int i, expand = 0; - - for (i = 0; i < priv->base_widths->len; i++) - { - if (priv->expand[i]) - expand++; - } - - return expand; -} - -static void -_gtk_cell_area_box_context_init (GtkCellAreaBoxContext *box_context) -{ - GtkCellAreaBoxContextPrivate *priv; - - box_context->priv = _gtk_cell_area_box_context_get_instance_private (box_context); - priv = box_context->priv; - - priv->base_widths = g_array_new (FALSE, TRUE, sizeof (CachedSize)); - priv->base_heights = g_array_new (FALSE, TRUE, sizeof (CachedSize)); - - priv->widths = g_hash_table_new_full (g_direct_hash, g_direct_equal, - NULL, (GDestroyNotify)free_cache_array); - priv->heights = g_hash_table_new_full (g_direct_hash, g_direct_equal, - NULL, (GDestroyNotify)free_cache_array); -} - -static void -_gtk_cell_area_box_context_class_init (GtkCellAreaBoxContextClass *class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (class); - GtkCellAreaContextClass *context_class = GTK_CELL_AREA_CONTEXT_CLASS (class); - - /* GObjectClass */ - object_class->finalize = _gtk_cell_area_box_context_finalize; - - context_class->reset = _gtk_cell_area_box_context_reset; - context_class->get_preferred_height_for_width = _gtk_cell_area_box_context_get_preferred_height_for_width; - context_class->get_preferred_width_for_height = _gtk_cell_area_box_context_get_preferred_width_for_height; -} - -/************************************************************* - * GObjectClass * - *************************************************************/ -static void -_gtk_cell_area_box_context_finalize (GObject *object) -{ - GtkCellAreaBoxContext *box_context = GTK_CELL_AREA_BOX_CONTEXT (object); - GtkCellAreaBoxContextPrivate *priv = box_context->priv; - - g_array_free (priv->base_widths, TRUE); - g_array_free (priv->base_heights, TRUE); - g_hash_table_destroy (priv->widths); - g_hash_table_destroy (priv->heights); - - g_free (priv->expand); - g_free (priv->align); - - G_OBJECT_CLASS (_gtk_cell_area_box_context_parent_class)->finalize (object); -} - -/************************************************************* - * GtkCellAreaContextClass * - *************************************************************/ -static void -_gtk_cell_area_box_context_reset (GtkCellAreaContext *context) -{ - GtkCellAreaBoxContext *box_context = GTK_CELL_AREA_BOX_CONTEXT (context); - GtkCellAreaBoxContextPrivate *priv = box_context->priv; - CachedSize *size; - int i; - - for (i = 0; i < priv->base_widths->len; i++) - { - size = &g_array_index (priv->base_widths, CachedSize, i); - - size->min_size = 0; - size->nat_size = 0; - - size = &g_array_index (priv->base_heights, CachedSize, i); - - size->min_size = 0; - size->nat_size = 0; - } - - /* Reset context sizes as well */ - g_hash_table_remove_all (priv->widths); - g_hash_table_remove_all (priv->heights); - - GTK_CELL_AREA_CONTEXT_CLASS - (_gtk_cell_area_box_context_parent_class)->reset (context); -} - -static void -_gtk_cell_area_box_context_sum (GtkCellAreaBoxContext *context, - GtkOrientation orientation, - int for_size, - int *minimum_size, - int *natural_size) -{ - GtkCellAreaBoxContextPrivate *priv = context->priv; - GtkCellAreaBox *area; - GtkOrientation box_orientation; - GArray *array; - int spacing, i, last_aligned_group_idx; - int min_size = 0, nat_size = 0; - - area = (GtkCellAreaBox *)gtk_cell_area_context_get_area (GTK_CELL_AREA_CONTEXT (context)); - spacing = gtk_cell_area_box_get_spacing (area); - box_orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (area)); - array = get_array (context, orientation, for_size); - - /* Get the last visible aligned group - * (we need to get space at least up till this group) */ - for (i = array->len - 1; i >= 0; i--) - { - if (priv->align[i] && - _gtk_cell_area_box_group_visible (area, i)) - break; - } - last_aligned_group_idx = i >= 0 ? i : 0; - - for (i = 0; i < array->len; i++) - { - CachedSize *size = &g_array_index (array, CachedSize, i); - - if (box_orientation == orientation) - { - if (i > last_aligned_group_idx && - !_gtk_cell_area_box_group_visible (area, i)) - continue; - - /* Dont add spacing for 0 size groups, they can be 0 size because - * they contain only invisible cells for this round of requests - */ - if (min_size > 0 && size->nat_size > 0) - { - min_size += spacing; - nat_size += spacing; - } - - min_size += size->min_size; - nat_size += size->nat_size; - } - else - { - min_size = MAX (min_size, size->min_size); - nat_size = MAX (nat_size, size->nat_size); - } - } - - if (for_size < 0) - { - if (orientation == GTK_ORIENTATION_HORIZONTAL) - gtk_cell_area_context_push_preferred_width (GTK_CELL_AREA_CONTEXT (context), min_size, nat_size); - else - gtk_cell_area_context_push_preferred_height (GTK_CELL_AREA_CONTEXT (context), min_size, nat_size); - } - - if (minimum_size) - *minimum_size = min_size; - if (natural_size) - *natural_size = nat_size; -} - -static void -_gtk_cell_area_box_context_get_preferred_height_for_width (GtkCellAreaContext *context, - int width, - int *minimum_height, - int *natural_height) -{ - _gtk_cell_area_box_context_sum (GTK_CELL_AREA_BOX_CONTEXT (context), GTK_ORIENTATION_VERTICAL, - width, minimum_height, natural_height); -} - -static void -_gtk_cell_area_box_context_get_preferred_width_for_height (GtkCellAreaContext *context, - int height, - int *minimum_width, - int *natural_width) -{ - _gtk_cell_area_box_context_sum (GTK_CELL_AREA_BOX_CONTEXT (context), GTK_ORIENTATION_HORIZONTAL, - height, minimum_width, natural_width); -} - -/************************************************************* - * API * - *************************************************************/ -static void -copy_size_array (GArray *src_array, - GArray *dest_array) -{ - int i; - - for (i = 0; i < src_array->len; i++) - { - CachedSize *src = &g_array_index (src_array, CachedSize, i); - CachedSize *dest = &g_array_index (dest_array, CachedSize, i); - - memcpy (dest, src, sizeof (CachedSize)); - } -} - -static void -for_size_copy (gpointer key, - GArray *size_array, - GHashTable *dest_hash) -{ - GArray *new_array; - - new_array = g_array_new (FALSE, TRUE, sizeof (CachedSize)); - g_array_set_size (new_array, size_array->len); - - copy_size_array (size_array, new_array); - - g_hash_table_insert (dest_hash, key, new_array); -} - -GtkCellAreaBoxContext * -_gtk_cell_area_box_context_copy (GtkCellAreaBox *box, - GtkCellAreaBoxContext *context) -{ - GtkCellAreaBoxContext *copy; - - copy = g_object_new (GTK_TYPE_CELL_AREA_BOX_CONTEXT, - "area", box, NULL); - - _gtk_cell_area_box_init_groups (copy, - context->priv->base_widths->len, - context->priv->expand, - context->priv->align); - - /* Copy the base arrays */ - copy_size_array (context->priv->base_widths, - copy->priv->base_widths); - copy_size_array (context->priv->base_heights, - copy->priv->base_heights); - - /* Copy each for size */ - g_hash_table_foreach (context->priv->heights, - (GHFunc)for_size_copy, copy->priv->heights); - g_hash_table_foreach (context->priv->widths, - (GHFunc)for_size_copy, copy->priv->widths); - - - return copy; -} - -void -_gtk_cell_area_box_init_groups (GtkCellAreaBoxContext *box_context, - guint n_groups, - gboolean *expand_groups, - gboolean *align_groups) -{ - GtkCellAreaBoxContextPrivate *priv; - gsize groups_size; - - g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context)); - g_return_if_fail (n_groups == 0 || expand_groups != NULL); - - /* When the group dimensions change, all info must be reset - * Note this already clears the min/nat values on the CachedSizes - */ - gtk_cell_area_context_reset (GTK_CELL_AREA_CONTEXT (box_context)); - - priv = box_context->priv; - g_array_set_size (priv->base_widths, n_groups); - g_array_set_size (priv->base_heights, n_groups); - - groups_size = n_groups * sizeof (gboolean); - - g_free (priv->expand); - priv->expand = g_memdup2 (expand_groups, groups_size); - - g_free (priv->align); - priv->align = g_memdup2 (align_groups, groups_size); -} - -void -_gtk_cell_area_box_context_push_group_width (GtkCellAreaBoxContext *box_context, - int group_idx, - int minimum_width, - int natural_width) -{ - GtkCellAreaBoxContextPrivate *priv; - CachedSize *size; - gboolean grew = FALSE; - - g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context)); - - priv = box_context->priv; - g_return_if_fail (group_idx < priv->base_widths->len); - - size = &g_array_index (priv->base_widths, CachedSize, group_idx); - if (minimum_width > size->min_size) - { - size->min_size = minimum_width; - grew = TRUE; - } - if (natural_width > size->nat_size) - { - size->nat_size = natural_width; - grew = TRUE; - } - - if (grew) - _gtk_cell_area_box_context_sum (box_context, GTK_ORIENTATION_HORIZONTAL, -1, NULL, NULL); -} - -void -_gtk_cell_area_box_context_push_group_height_for_width (GtkCellAreaBoxContext *box_context, - int group_idx, - int for_width, - int minimum_height, - int natural_height) -{ - GtkCellAreaBoxContextPrivate *priv; - GArray *group_array; - CachedSize *size; - - g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context)); - - priv = box_context->priv; - g_return_if_fail (group_idx < priv->base_widths->len); - - group_array = g_hash_table_lookup (priv->heights, GINT_TO_POINTER (for_width)); - if (!group_array) - { - group_array = group_array_new (box_context); - g_hash_table_insert (priv->heights, GINT_TO_POINTER (for_width), group_array); - } - - size = &g_array_index (group_array, CachedSize, group_idx); - size->min_size = MAX (size->min_size, minimum_height); - size->nat_size = MAX (size->nat_size, natural_height); -} - -void -_gtk_cell_area_box_context_push_group_height (GtkCellAreaBoxContext *box_context, - int group_idx, - int minimum_height, - int natural_height) -{ - GtkCellAreaBoxContextPrivate *priv; - CachedSize *size; - gboolean grew = FALSE; - - g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context)); - - priv = box_context->priv; - g_return_if_fail (group_idx < priv->base_heights->len); - - size = &g_array_index (priv->base_heights, CachedSize, group_idx); - if (minimum_height > size->min_size) - { - size->min_size = minimum_height; - grew = TRUE; - } - if (natural_height > size->nat_size) - { - size->nat_size = natural_height; - grew = TRUE; - } - - if (grew) - _gtk_cell_area_box_context_sum (box_context, GTK_ORIENTATION_VERTICAL, -1, NULL, NULL); -} - -void -_gtk_cell_area_box_context_push_group_width_for_height (GtkCellAreaBoxContext *box_context, - int group_idx, - int for_height, - int minimum_width, - int natural_width) -{ - GtkCellAreaBoxContextPrivate *priv; - GArray *group_array; - CachedSize *size; - - g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context)); - - priv = box_context->priv; - g_return_if_fail (group_idx < priv->base_widths->len); - - group_array = g_hash_table_lookup (priv->widths, GINT_TO_POINTER (for_height)); - if (!group_array) - { - group_array = group_array_new (box_context); - g_hash_table_insert (priv->widths, GINT_TO_POINTER (for_height), group_array); - } - - size = &g_array_index (group_array, CachedSize, group_idx); - size->min_size = MAX (size->min_size, minimum_width); - size->nat_size = MAX (size->nat_size, natural_width); -} - -void -_gtk_cell_area_box_context_get_group_width (GtkCellAreaBoxContext *box_context, - int group_idx, - int *minimum_width, - int *natural_width) -{ - GtkCellAreaBoxContextPrivate *priv; - CachedSize *size; - - g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context)); - - priv = box_context->priv; - g_return_if_fail (group_idx < priv->base_widths->len); - - size = &g_array_index (priv->base_widths, CachedSize, group_idx); - - if (minimum_width) - *minimum_width = size->min_size; - - if (natural_width) - *natural_width = size->nat_size; -} - -void -_gtk_cell_area_box_context_get_group_height_for_width (GtkCellAreaBoxContext *box_context, - int group_idx, - int for_width, - int *minimum_height, - int *natural_height) -{ - GtkCellAreaBoxContextPrivate *priv; - GArray *group_array; - - g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context)); - - priv = box_context->priv; - g_return_if_fail (group_idx < priv->base_widths->len); - - group_array = g_hash_table_lookup (priv->heights, GINT_TO_POINTER (for_width)); - - if (group_array) - { - CachedSize *size = &g_array_index (group_array, CachedSize, group_idx); - - if (minimum_height) - *minimum_height = size->min_size; - - if (natural_height) - *natural_height = size->nat_size; - } - else - { - if (minimum_height) - *minimum_height = -1; - - if (natural_height) - *natural_height = -1; - } -} - -void -_gtk_cell_area_box_context_get_group_height (GtkCellAreaBoxContext *box_context, - int group_idx, - int *minimum_height, - int *natural_height) -{ - GtkCellAreaBoxContextPrivate *priv; - CachedSize *size; - - g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context)); - - priv = box_context->priv; - g_return_if_fail (group_idx < priv->base_heights->len); - - size = &g_array_index (priv->base_heights, CachedSize, group_idx); - - if (minimum_height) - *minimum_height = size->min_size; - - if (natural_height) - *natural_height = size->nat_size; -} - -void -_gtk_cell_area_box_context_get_group_width_for_height (GtkCellAreaBoxContext *box_context, - int group_idx, - int for_height, - int *minimum_width, - int *natural_width) -{ - GtkCellAreaBoxContextPrivate *priv; - GArray *group_array; - - g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context)); - - priv = box_context->priv; - g_return_if_fail (group_idx < priv->base_widths->len); - - group_array = g_hash_table_lookup (priv->widths, GINT_TO_POINTER (for_height)); - - if (group_array) - { - CachedSize *size = &g_array_index (group_array, CachedSize, group_idx); - - if (minimum_width) - *minimum_width = size->min_size; - - if (natural_width) - *natural_width = size->nat_size; - } - else - { - if (minimum_width) - *minimum_width = -1; - - if (natural_width) - *natural_width = -1; - } -} - -static GtkRequestedSize * -_gtk_cell_area_box_context_get_requests (GtkCellAreaBoxContext *box_context, - GtkCellAreaBox *area, - GtkOrientation orientation, - int for_size, - int *n_requests) -{ - GtkCellAreaBoxContextPrivate *priv = box_context->priv; - GtkRequestedSize *requests; - GArray *array; - CachedSize *size; - int visible_groups = 0; - int last_aligned_group_idx = 0; - int i, j; - - /* Get the last visible aligned group - * (we need to get space at least up till this group) */ - for (i = priv->base_widths->len - 1; i >= 0; i--) - { - if (priv->align[i] && - _gtk_cell_area_box_group_visible (area, i)) - break; - } - last_aligned_group_idx = i >= 0 ? i : 0; - - priv = box_context->priv; - array = get_array (box_context, orientation, for_size); - - for (i = 0; i < array->len; i++) - { - size = &g_array_index (array, CachedSize, i); - - if (size->nat_size > 0 && - (i <= last_aligned_group_idx || - _gtk_cell_area_box_group_visible (area, i))) - visible_groups++; - } - - requests = g_new (GtkRequestedSize, visible_groups); - - for (j = 0, i = 0; i < array->len; i++) - { - size = &g_array_index (array, CachedSize, i); - - if (size->nat_size > 0 && - (i <= last_aligned_group_idx || - _gtk_cell_area_box_group_visible (area, i))) - { - requests[j].data = GINT_TO_POINTER (i); - requests[j].minimum_size = size->min_size; - requests[j].natural_size = size->nat_size; - j++; - } - } - - if (n_requests) - *n_requests = visible_groups; - - return requests; -} - -static GtkCellAreaBoxAllocation * -allocate_for_orientation (GtkCellAreaBoxContext *context, - GtkCellAreaBox *area, - GtkOrientation orientation, - int spacing, - int size, - int for_size, - int *n_allocs) -{ - GtkCellAreaBoxContextPrivate *priv = context->priv; - GtkCellAreaBoxAllocation *allocs; - GtkRequestedSize *sizes; - int n_expand_groups = 0; - int i, n_groups, position, vis_position; - int extra_size, extra_extra; - int avail_size = size; - - sizes = _gtk_cell_area_box_context_get_requests (context, area, orientation, for_size, &n_groups); - n_expand_groups = count_expand_groups (context); - - /* First start by naturally allocating space among groups */ - avail_size -= (n_groups - 1) * spacing; - for (i = 0; i < n_groups; i++) - avail_size -= sizes[i].minimum_size; - - if (avail_size > 0) - avail_size = gtk_distribute_natural_allocation (avail_size, n_groups, sizes); - else - avail_size = 0; - - /* Calculate/distribute expand for groups */ - if (n_expand_groups > 0) - { - extra_size = avail_size / n_expand_groups; - extra_extra = avail_size % n_expand_groups; - } - else - extra_size = extra_extra = 0; - - allocs = g_new (GtkCellAreaBoxAllocation, n_groups); - - for (vis_position = 0, position = 0, i = 0; i < n_groups; i++) - { - allocs[i].group_idx = GPOINTER_TO_INT (sizes[i].data); - - if (priv->align[allocs[i].group_idx]) - vis_position = position; - - allocs[i].position = vis_position; - allocs[i].size = sizes[i].minimum_size; - - if (group_expands (context, allocs[i].group_idx)) - { - allocs[i].size += extra_size; - if (extra_extra) - { - allocs[i].size++; - extra_extra--; - } - } - - position += allocs[i].size; - position += spacing; - - if (_gtk_cell_area_box_group_visible (area, allocs[i].group_idx)) - { - vis_position += allocs[i].size; - vis_position += spacing; - } - } - - if (n_allocs) - *n_allocs = n_groups; - - g_free (sizes); - - return allocs; -} - -GtkRequestedSize * -_gtk_cell_area_box_context_get_widths (GtkCellAreaBoxContext *box_context, - int *n_widths) -{ - GtkCellAreaBox *area = (GtkCellAreaBox *)gtk_cell_area_context_get_area (GTK_CELL_AREA_CONTEXT (box_context)); - - return _gtk_cell_area_box_context_get_requests (box_context, area, GTK_ORIENTATION_HORIZONTAL, -1, n_widths); -} - -GtkRequestedSize * -_gtk_cell_area_box_context_get_heights (GtkCellAreaBoxContext *box_context, - int *n_heights) -{ - GtkCellAreaBox *area = (GtkCellAreaBox *)gtk_cell_area_context_get_area (GTK_CELL_AREA_CONTEXT (box_context)); - - return _gtk_cell_area_box_context_get_requests (box_context, area, GTK_ORIENTATION_VERTICAL, -1, n_heights); -} - -GtkCellAreaBoxAllocation * -_gtk_cell_area_box_context_get_orientation_allocs (GtkCellAreaBoxContext *context, - int *n_allocs) -{ - GtkCellAreaContext *ctx = GTK_CELL_AREA_CONTEXT (context); - GtkCellAreaBox *area; - GtkOrientation orientation; - int spacing, width, height, alloc_count = 0; - GtkCellAreaBoxAllocation *allocs = NULL; - - area = (GtkCellAreaBox *)gtk_cell_area_context_get_area (ctx); - orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (area)); - spacing = gtk_cell_area_box_get_spacing (area); - - gtk_cell_area_context_get_allocation (ctx, &width, &height); - - if (orientation == GTK_ORIENTATION_HORIZONTAL && width > 0) - allocs = allocate_for_orientation (context, area, orientation, - spacing, width, height, - &alloc_count); - else if (orientation == GTK_ORIENTATION_VERTICAL && height > 0) - allocs = allocate_for_orientation (context, area, orientation, - spacing, height, width, - &alloc_count); - - *n_allocs = alloc_count; - - return allocs; -} diff --git a/gtk/gtkcellareaboxcontextprivate.h b/gtk/gtkcellareaboxcontextprivate.h deleted file mode 100644 index 104a0218be..0000000000 --- a/gtk/gtkcellareaboxcontextprivate.h +++ /dev/null @@ -1,137 +0,0 @@ -/* gtkcellareaboxcontext.h - * - * Copyright (C) 2010 Openismus GmbH - * - * Authors: - * Tristan Van Berkom - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#ifndef __GTK_CELL_AREA_BOX_CONTEXT_H__ -#define __GTK_CELL_AREA_BOX_CONTEXT_H__ - -#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) -#error "Only can be included directly." -#endif - -#include -#include -#include -#include - -G_BEGIN_DECLS - -#define GTK_TYPE_CELL_AREA_BOX_CONTEXT (_gtk_cell_area_box_context_get_type ()) -#define GTK_CELL_AREA_BOX_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_AREA_BOX_CONTEXT, GtkCellAreaBoxContext)) -#define GTK_CELL_AREA_BOX_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_CELL_AREA_BOX_CONTEXT, GtkCellAreaBoxContextClass)) -#define GTK_IS_CELL_AREA_BOX_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_AREA_BOX_CONTEXT)) -#define GTK_IS_CELL_AREA_BOX_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_CELL_AREA_BOX_CONTEXT)) -#define GTK_CELL_AREA_BOX_CONTEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CELL_AREA_BOX_CONTEXT, GtkCellAreaBoxContextClass)) - -typedef struct _GtkCellAreaBoxContext GtkCellAreaBoxContext; -typedef struct _GtkCellAreaBoxContextClass GtkCellAreaBoxContextClass; -typedef struct _GtkCellAreaBoxContextPrivate GtkCellAreaBoxContextPrivate; - -struct _GtkCellAreaBoxContext -{ - GtkCellAreaContext parent_instance; - - GtkCellAreaBoxContextPrivate *priv; -}; - -struct _GtkCellAreaBoxContextClass -{ - GtkCellAreaContextClass parent_class; - -}; - -GType _gtk_cell_area_box_context_get_type (void) G_GNUC_CONST; - - -/* Create a duplicate of the context */ -GtkCellAreaBoxContext *_gtk_cell_area_box_context_copy (GtkCellAreaBox *box, - GtkCellAreaBoxContext *box_context); - -/* Initialize group array dimensions */ -void _gtk_cell_area_box_init_groups (GtkCellAreaBoxContext *box_context, - guint n_groups, - gboolean *expand_groups, - gboolean *align_groups); - -/* Update cell-group sizes */ -void _gtk_cell_area_box_context_push_group_width (GtkCellAreaBoxContext *box_context, - int group_idx, - int minimum_width, - int natural_width); - -void _gtk_cell_area_box_context_push_group_height_for_width (GtkCellAreaBoxContext *box_context, - int group_idx, - int for_width, - int minimum_height, - int natural_height); - -void _gtk_cell_area_box_context_push_group_height (GtkCellAreaBoxContext *box_context, - int group_idx, - int minimum_height, - int natural_height); - -void _gtk_cell_area_box_context_push_group_width_for_height (GtkCellAreaBoxContext *box_context, - int group_idx, - int for_height, - int minimum_width, - int natural_width); - -/* Fetch cell-group sizes */ -void _gtk_cell_area_box_context_get_group_width (GtkCellAreaBoxContext *box_context, - int group_idx, - int *minimum_width, - int *natural_width); - -void _gtk_cell_area_box_context_get_group_height_for_width (GtkCellAreaBoxContext *box_context, - int group_idx, - int for_width, - int *minimum_height, - int *natural_height); - -void _gtk_cell_area_box_context_get_group_height (GtkCellAreaBoxContext *box_context, - int group_idx, - int *minimum_height, - int *natural_height); - -void _gtk_cell_area_box_context_get_group_width_for_height (GtkCellAreaBoxContext *box_context, - int group_idx, - int for_height, - int *minimum_width, - int *natural_width); - -GtkRequestedSize *_gtk_cell_area_box_context_get_widths (GtkCellAreaBoxContext *box_context, - int *n_widths); -GtkRequestedSize *_gtk_cell_area_box_context_get_heights (GtkCellAreaBoxContext *box_context, - int *n_heights); - -/* Private context/area interaction */ -typedef struct { - int group_idx; /* Groups containing only invisible cells are not allocated */ - int position; /* Relative group allocation position in the orientation of the box */ - int size; /* Full allocated size of the cells in this group spacing inclusive */ -} GtkCellAreaBoxAllocation; - -GtkCellAreaBoxAllocation * -_gtk_cell_area_box_context_get_orientation_allocs (GtkCellAreaBoxContext *context, - int *n_allocs); - -G_END_DECLS - -#endif /* __GTK_CELL_AREA_BOX_CONTEXT_H__ */ diff --git a/gtk/gtkcellareacontext.c b/gtk/gtkcellareacontext.c deleted file mode 100644 index d562400142..0000000000 --- a/gtk/gtkcellareacontext.c +++ /dev/null @@ -1,606 +0,0 @@ -/* gtkcellareacontext.c - * - * Copyright (C) 2010 Openismus GmbH - * - * Authors: - * Tristan Van Berkom - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -/** - * GtkCellAreaContext: - * - * Stores geometrical information for a series of rows in a GtkCellArea - * - * The `GtkCellAreaContext` object is created by a given `GtkCellArea` - * implementation via its `GtkCellAreaClass.create_context()` virtual - * method and is used to store cell sizes and alignments for a series of - * `GtkTreeModel` rows that are requested and rendered in the same context. - * - * `GtkCellLayout` widgets can create any number of contexts in which to - * request and render groups of data rows. However, it’s important that the - * same context which was used to request sizes for a given `GtkTreeModel` - * row also be used for the same row when calling other `GtkCellArea` APIs - * such as gtk_cell_area_render() and gtk_cell_area_event(). - */ - -#include "config.h" -#include "gtkmarshalers.h" -#include "gtkcellareacontext.h" -#include "gtkprivate.h" - -/* GObjectClass */ -static void gtk_cell_area_context_dispose (GObject *object); -static void gtk_cell_area_context_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec); -static void gtk_cell_area_context_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec); - -/* GtkCellAreaContextClass */ -static void gtk_cell_area_context_real_reset (GtkCellAreaContext *context); -static void gtk_cell_area_context_real_allocate (GtkCellAreaContext *context, - int width, - int height); - -typedef struct _GtkCellAreaContextPrivate GtkCellAreaContextPrivate; -struct _GtkCellAreaContextPrivate -{ - GtkCellArea *cell_area; - - int min_width; - int nat_width; - int min_height; - int nat_height; - int alloc_width; - int alloc_height; -}; - -enum { - PROP_0, - PROP_CELL_AREA, - PROP_MIN_WIDTH, - PROP_NAT_WIDTH, - PROP_MIN_HEIGHT, - PROP_NAT_HEIGHT -}; - -G_DEFINE_TYPE_WITH_PRIVATE (GtkCellAreaContext, gtk_cell_area_context, G_TYPE_OBJECT) - -static void -gtk_cell_area_context_init (GtkCellAreaContext *context) -{ -} - -static void -gtk_cell_area_context_class_init (GtkCellAreaContextClass *class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (class); - - /* GObjectClass */ - object_class->dispose = gtk_cell_area_context_dispose; - object_class->get_property = gtk_cell_area_context_get_property; - object_class->set_property = gtk_cell_area_context_set_property; - - /* GtkCellAreaContextClass */ - class->reset = gtk_cell_area_context_real_reset; - class->allocate = gtk_cell_area_context_real_allocate; - - /** - * GtkCellAreaContext:area: - * - * The `GtkCellArea` this context was created by - */ - g_object_class_install_property (object_class, - PROP_CELL_AREA, - g_param_spec_object ("area", NULL, NULL, - GTK_TYPE_CELL_AREA, - GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - - /** - * GtkCellAreaContext:minimum-width: - * - * The minimum width for the `GtkCellArea` in this context - * for all `GtkTreeModel` rows that this context was requested - * for using gtk_cell_area_get_preferred_width(). - */ - g_object_class_install_property (object_class, - PROP_MIN_WIDTH, - g_param_spec_int ("minimum-width", NULL, NULL, - -1, G_MAXINT, -1, - GTK_PARAM_READABLE)); - - /** - * GtkCellAreaContext:natural-width: - * - * The natural width for the `GtkCellArea` in this context - * for all `GtkTreeModel` rows that this context was requested - * for using gtk_cell_area_get_preferred_width(). - */ - g_object_class_install_property (object_class, - PROP_NAT_WIDTH, - g_param_spec_int ("natural-width", NULL, NULL, - -1, G_MAXINT, -1, - GTK_PARAM_READABLE)); - - /** - * GtkCellAreaContext:minimum-height: - * - * The minimum height for the `GtkCellArea` in this context - * for all `GtkTreeModel` rows that this context was requested - * for using gtk_cell_area_get_preferred_height(). - */ - g_object_class_install_property (object_class, - PROP_MIN_HEIGHT, - g_param_spec_int ("minimum-height", NULL, NULL, - -1, G_MAXINT, -1, - GTK_PARAM_READABLE)); - - /** - * GtkCellAreaContext:natural-height: - * - * The natural height for the `GtkCellArea` in this context - * for all `GtkTreeModel` rows that this context was requested - * for using gtk_cell_area_get_preferred_height(). - */ - g_object_class_install_property (object_class, - PROP_NAT_HEIGHT, - g_param_spec_int ("natural-height", NULL, NULL, - -1, G_MAXINT, -1, - GTK_PARAM_READABLE)); -} - -/************************************************************* - * GObjectClass * - *************************************************************/ -static void -gtk_cell_area_context_dispose (GObject *object) -{ - GtkCellAreaContext *context = GTK_CELL_AREA_CONTEXT (object); - GtkCellAreaContextPrivate *priv = gtk_cell_area_context_get_instance_private (context); - - if (priv->cell_area) - { - g_object_unref (priv->cell_area); - - priv->cell_area = NULL; - } - - G_OBJECT_CLASS (gtk_cell_area_context_parent_class)->dispose (object); -} - -static void -gtk_cell_area_context_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - GtkCellAreaContext *context = GTK_CELL_AREA_CONTEXT (object); - GtkCellAreaContextPrivate *priv = gtk_cell_area_context_get_instance_private (context); - - switch (prop_id) - { - case PROP_CELL_AREA: - priv->cell_area = g_value_dup_object (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gtk_cell_area_context_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GtkCellAreaContext *context = GTK_CELL_AREA_CONTEXT (object); - GtkCellAreaContextPrivate *priv = gtk_cell_area_context_get_instance_private (context); - - switch (prop_id) - { - case PROP_CELL_AREA: - g_value_set_object (value, priv->cell_area); - break; - case PROP_MIN_WIDTH: - g_value_set_int (value, priv->min_width); - break; - case PROP_NAT_WIDTH: - g_value_set_int (value, priv->nat_width); - break; - case PROP_MIN_HEIGHT: - g_value_set_int (value, priv->min_height); - break; - case PROP_NAT_HEIGHT: - g_value_set_int (value, priv->nat_height); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -/************************************************************* - * GtkCellAreaContextClass * - *************************************************************/ -static void -gtk_cell_area_context_real_reset (GtkCellAreaContext *context) -{ - GtkCellAreaContextPrivate *priv = gtk_cell_area_context_get_instance_private (context); - - g_object_freeze_notify (G_OBJECT (context)); - - if (priv->min_width != 0) - { - priv->min_width = 0; - g_object_notify (G_OBJECT (context), "minimum-width"); - } - - if (priv->nat_width != 0) - { - priv->nat_width = 0; - g_object_notify (G_OBJECT (context), "natural-width"); - } - - if (priv->min_height != 0) - { - priv->min_height = 0; - g_object_notify (G_OBJECT (context), "minimum-height"); - } - - if (priv->nat_height != 0) - { - priv->nat_height = 0; - g_object_notify (G_OBJECT (context), "natural-height"); - } - - priv->alloc_width = 0; - priv->alloc_height = 0; - - g_object_thaw_notify (G_OBJECT (context)); -} - -static void -gtk_cell_area_context_real_allocate (GtkCellAreaContext *context, - int width, - int height) -{ - GtkCellAreaContextPrivate *priv = gtk_cell_area_context_get_instance_private (context); - - priv->alloc_width = width; - priv->alloc_height = height; -} - -/************************************************************* - * API * - *************************************************************/ -/** - * gtk_cell_area_context_get_area: - * @context: a `GtkCellAreaContext` - * - * Fetches the `GtkCellArea` this @context was created by. - * - * This is generally unneeded by layouting widgets; however, - * it is important for the context implementation itself to - * fetch information about the area it is being used for. - * - * For instance at `GtkCellAreaContextClass.allocate()` time - * it’s important to know details about any cell spacing - * that the `GtkCellArea` is configured with in order to - * compute a proper allocation. - * - * Returns: (transfer none): the `GtkCellArea` this context was created by. - */ -GtkCellArea * -gtk_cell_area_context_get_area (GtkCellAreaContext *context) -{ - GtkCellAreaContextPrivate *priv = gtk_cell_area_context_get_instance_private (context); - - g_return_val_if_fail (GTK_IS_CELL_AREA_CONTEXT (context), NULL); - - return priv->cell_area; -} - -/** - * gtk_cell_area_context_reset: - * @context: a `GtkCellAreaContext` - * - * Resets any previously cached request and allocation - * data. - * - * When underlying `GtkTreeModel` data changes its - * important to reset the context if the content - * size is allowed to shrink. If the content size - * is only allowed to grow (this is usually an option - * for views rendering large data stores as a measure - * of optimization), then only the row that changed - * or was inserted needs to be (re)requested with - * gtk_cell_area_get_preferred_width(). - * - * When the new overall size of the context requires - * that the allocated size changes (or whenever this - * allocation changes at all), the variable row - * sizes need to be re-requested for every row. - * - * For instance, if the rows are displayed all with - * the same width from top to bottom then a change - * in the allocated width necessitates a recalculation - * of all the displayed row heights using - * gtk_cell_area_get_preferred_height_for_width(). - */ -void -gtk_cell_area_context_reset (GtkCellAreaContext *context) -{ - g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context)); - - GTK_CELL_AREA_CONTEXT_GET_CLASS (context)->reset (context); -} - -/** - * gtk_cell_area_context_allocate: - * @context: a `GtkCellAreaContext` - * @width: the allocated width for all `GtkTreeModel` rows rendered - * with @context, or -1 - * @height: the allocated height for all `GtkTreeModel` rows rendered - * with @context, or -1 - * - * Allocates a width and/or a height for all rows which are to be - * rendered with @context. - * - * Usually allocation is performed only horizontally or sometimes - * vertically since a group of rows are usually rendered side by - * side vertically or horizontally and share either the same width - * or the same height. Sometimes they are allocated in both horizontal - * and vertical orientations producing a homogeneous effect of the - * rows. This is generally the case for `GtkTreeView` when - * `GtkTreeView:fixed-height-mode` is enabled. - */ -void -gtk_cell_area_context_allocate (GtkCellAreaContext *context, - int width, - int height) -{ - g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context)); - - GTK_CELL_AREA_CONTEXT_GET_CLASS (context)->allocate (context, width, height); -} - -/** - * gtk_cell_area_context_get_preferred_width: - * @context: a `GtkCellAreaContext` - * @minimum_width: (out) (optional): location to store the minimum width - * @natural_width: (out) (optional): location to store the natural width - * - * Gets the accumulative preferred width for all rows which have been - * requested with this context. - * - * After gtk_cell_area_context_reset() is called and/or before ever - * requesting the size of a `GtkCellArea`, the returned values are 0. - */ -void -gtk_cell_area_context_get_preferred_width (GtkCellAreaContext *context, - int *minimum_width, - int *natural_width) -{ - GtkCellAreaContextPrivate *priv = gtk_cell_area_context_get_instance_private (context); - - g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context)); - - if (minimum_width) - *minimum_width = priv->min_width; - - if (natural_width) - *natural_width = priv->nat_width; -} - -/** - * gtk_cell_area_context_get_preferred_height: - * @context: a `GtkCellAreaContext` - * @minimum_height: (out) (optional): location to store the minimum height - * @natural_height: (out) (optional): location to store the natural height - * - * Gets the accumulative preferred height for all rows which have been - * requested with this context. - * - * After gtk_cell_area_context_reset() is called and/or before ever - * requesting the size of a `GtkCellArea`, the returned values are 0. - */ -void -gtk_cell_area_context_get_preferred_height (GtkCellAreaContext *context, - int *minimum_height, - int *natural_height) -{ - GtkCellAreaContextPrivate *priv = gtk_cell_area_context_get_instance_private (context); - - g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context)); - - if (minimum_height) - *minimum_height = priv->min_height; - - if (natural_height) - *natural_height = priv->nat_height; -} - -/** - * gtk_cell_area_context_get_preferred_height_for_width: - * @context: a `GtkCellAreaContext` - * @width: a proposed width for allocation - * @minimum_height: (out) (optional): location to store the minimum height - * @natural_height: (out) (optional): location to store the natural height - * - * Gets the accumulative preferred height for @width for all rows - * which have been requested for the same said @width with this context. - * - * After gtk_cell_area_context_reset() is called and/or before ever - * requesting the size of a `GtkCellArea`, the returned values are -1. - */ -void -gtk_cell_area_context_get_preferred_height_for_width (GtkCellAreaContext *context, - int width, - int *minimum_height, - int *natural_height) -{ - g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context)); - - if (GTK_CELL_AREA_CONTEXT_GET_CLASS (context)->get_preferred_height_for_width) - GTK_CELL_AREA_CONTEXT_GET_CLASS (context)->get_preferred_height_for_width (context, - width, - minimum_height, - natural_height); -} - -/** - * gtk_cell_area_context_get_preferred_width_for_height: - * @context: a `GtkCellAreaContext` - * @height: a proposed height for allocation - * @minimum_width: (out) (optional): location to store the minimum width - * @natural_width: (out) (optional): location to store the natural width - * - * Gets the accumulative preferred width for @height for all rows which - * have been requested for the same said @height with this context. - * - * After gtk_cell_area_context_reset() is called and/or before ever - * requesting the size of a `GtkCellArea`, the returned values are -1. - */ -void -gtk_cell_area_context_get_preferred_width_for_height (GtkCellAreaContext *context, - int height, - int *minimum_width, - int *natural_width) -{ - g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context)); - - if (GTK_CELL_AREA_CONTEXT_GET_CLASS (context)->get_preferred_width_for_height) - GTK_CELL_AREA_CONTEXT_GET_CLASS (context)->get_preferred_width_for_height (context, - height, - minimum_width, - natural_width); -} - -/** - * gtk_cell_area_context_get_allocation: - * @context: a `GtkCellAreaContext` - * @width: (out) (optional): location to store the allocated width - * @height: (out) (optional): location to store the allocated height - * - * Fetches the current allocation size for @context. - * - * If the context was not allocated in width or height, or if the - * context was recently reset with gtk_cell_area_context_reset(), - * the returned value will be -1. - */ -void -gtk_cell_area_context_get_allocation (GtkCellAreaContext *context, - int *width, - int *height) -{ - GtkCellAreaContextPrivate *priv = gtk_cell_area_context_get_instance_private (context); - - g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context)); - - if (width) - *width = priv->alloc_width; - - if (height) - *height = priv->alloc_height; -} - -/** - * gtk_cell_area_context_push_preferred_width: - * @context: a `GtkCellAreaContext` - * @minimum_width: the proposed new minimum width for @context - * @natural_width: the proposed new natural width for @context - * - * Causes the minimum and/or natural width to grow if the new - * proposed sizes exceed the current minimum and natural width. - * - * This is used by `GtkCellAreaContext` implementations during - * the request process over a series of `GtkTreeModel` rows to - * progressively push the requested width over a series of - * gtk_cell_area_get_preferred_width() requests. - */ -void -gtk_cell_area_context_push_preferred_width (GtkCellAreaContext *context, - int minimum_width, - int natural_width) -{ - GtkCellAreaContextPrivate *priv = gtk_cell_area_context_get_instance_private (context); - - g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context)); - - g_object_freeze_notify (G_OBJECT (context)); - - if (minimum_width > priv->min_width) - { - priv->min_width = minimum_width; - - g_object_notify (G_OBJECT (context), "minimum-width"); - } - - if (natural_width > priv->nat_width) - { - priv->nat_width = natural_width; - - g_object_notify (G_OBJECT (context), "natural-width"); - } - - g_object_thaw_notify (G_OBJECT (context)); -} - -/** - * gtk_cell_area_context_push_preferred_height: - * @context: a `GtkCellAreaContext` - * @minimum_height: the proposed new minimum height for @context - * @natural_height: the proposed new natural height for @context - * - * Causes the minimum and/or natural height to grow if the new - * proposed sizes exceed the current minimum and natural height. - * - * This is used by `GtkCellAreaContext` implementations during - * the request process over a series of `GtkTreeModel` rows to - * progressively push the requested height over a series of - * gtk_cell_area_get_preferred_height() requests. - */ -void -gtk_cell_area_context_push_preferred_height (GtkCellAreaContext *context, - int minimum_height, - int natural_height) -{ - GtkCellAreaContextPrivate *priv = gtk_cell_area_context_get_instance_private (context); - - g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context)); - - g_object_freeze_notify (G_OBJECT (context)); - - if (minimum_height > priv->min_height) - { - priv->min_height = minimum_height; - - g_object_notify (G_OBJECT (context), "minimum-height"); - } - - if (natural_height > priv->nat_height) - { - priv->nat_height = natural_height; - - g_object_notify (G_OBJECT (context), "natural-height"); - } - - g_object_thaw_notify (G_OBJECT (context)); -} diff --git a/gtk/gtkcellareacontext.h b/gtk/gtkcellareacontext.h deleted file mode 100644 index 953a1837db..0000000000 --- a/gtk/gtkcellareacontext.h +++ /dev/null @@ -1,141 +0,0 @@ -/* gtkcellareacontext.h - * - * Copyright (C) 2010 Openismus GmbH - * - * Authors: - * Tristan Van Berkom - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#ifndef __GTK_CELL_AREA_CONTEXT_H__ -#define __GTK_CELL_AREA_CONTEXT_H__ - -#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) -#error "Only can be included directly." -#endif - -#include - -G_BEGIN_DECLS - -#define GTK_TYPE_CELL_AREA_CONTEXT (gtk_cell_area_context_get_type ()) -#define GTK_CELL_AREA_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_AREA_CONTEXT, GtkCellAreaContext)) -#define GTK_CELL_AREA_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_CELL_AREA_CONTEXT, GtkCellAreaContextClass)) -#define GTK_IS_CELL_AREA_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_AREA_CONTEXT)) -#define GTK_IS_CELL_AREA_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_CELL_AREA_CONTEXT)) -#define GTK_CELL_AREA_CONTEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CELL_AREA_CONTEXT, GtkCellAreaContextClass)) - -typedef struct _GtkCellAreaContextPrivate GtkCellAreaContextPrivate; -typedef struct _GtkCellAreaContextClass GtkCellAreaContextClass; - -struct _GtkCellAreaContext -{ - /*< private >*/ - GObject parent_instance; -}; - -/** - * GtkCellAreaContextClass: - * @allocate: This tells the context that an allocation width or height - * (or both) have been decided for a group of rows. The context should - * store any allocations for internally aligned cells at this point so - * that they dont need to be recalculated at gtk_cell_area_render() time. - * @reset: Clear any previously stored information about requested and - * allocated sizes for the context. - * @get_preferred_height_for_width: Returns the aligned height for the given - * width that context must store while collecting sizes for it’s rows. - * @get_preferred_width_for_height: Returns the aligned width for the given - * height that context must store while collecting sizes for it’s rows. - */ -struct _GtkCellAreaContextClass -{ - /*< private >*/ - GObjectClass parent_class; - - /*< public >*/ - void (* allocate) (GtkCellAreaContext *context, - int width, - int height); - void (* reset) (GtkCellAreaContext *context); - void (* get_preferred_height_for_width) (GtkCellAreaContext *context, - int width, - int *minimum_height, - int *natural_height); - void (* get_preferred_width_for_height) (GtkCellAreaContext *context, - int height, - int *minimum_width, - int *natural_width); - - /*< private >*/ - - gpointer padding[8]; -}; - -GDK_AVAILABLE_IN_ALL -GType gtk_cell_area_context_get_type (void) G_GNUC_CONST; - -/* Main apis */ -GDK_AVAILABLE_IN_ALL -GtkCellArea *gtk_cell_area_context_get_area (GtkCellAreaContext *context); -GDK_AVAILABLE_IN_ALL -void gtk_cell_area_context_allocate (GtkCellAreaContext *context, - int width, - int height); -GDK_AVAILABLE_IN_ALL -void gtk_cell_area_context_reset (GtkCellAreaContext *context); - -/* Apis for GtkCellArea clients to consult cached values - * for a series of GtkTreeModel rows - */ -GDK_AVAILABLE_IN_ALL -void gtk_cell_area_context_get_preferred_width (GtkCellAreaContext *context, - int *minimum_width, - int *natural_width); -GDK_AVAILABLE_IN_ALL -void gtk_cell_area_context_get_preferred_height (GtkCellAreaContext *context, - int *minimum_height, - int *natural_height); -GDK_AVAILABLE_IN_ALL -void gtk_cell_area_context_get_preferred_height_for_width (GtkCellAreaContext *context, - int width, - int *minimum_height, - int *natural_height); -GDK_AVAILABLE_IN_ALL -void gtk_cell_area_context_get_preferred_width_for_height (GtkCellAreaContext *context, - int height, - int *minimum_width, - int *natural_width); -GDK_AVAILABLE_IN_ALL -void gtk_cell_area_context_get_allocation (GtkCellAreaContext *context, - int *width, - int *height); - -/* Apis for GtkCellArea implementations to update cached values - * for multiple GtkTreeModel rows - */ -GDK_AVAILABLE_IN_ALL -void gtk_cell_area_context_push_preferred_width (GtkCellAreaContext *context, - int minimum_width, - int natural_width); -GDK_AVAILABLE_IN_ALL -void gtk_cell_area_context_push_preferred_height (GtkCellAreaContext *context, - int minimum_height, - int natural_height); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkCellAreaContext, g_object_unref) - -G_END_DECLS - -#endif /* __GTK_CELL_AREA_CONTEXT_H__ */ diff --git a/gtk/gtkcelleditable.c b/gtk/gtkcelleditable.c deleted file mode 100644 index facd4e85bb..0000000000 --- a/gtk/gtkcelleditable.c +++ /dev/null @@ -1,152 +0,0 @@ -/* gtkcelleditable.c - * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -/** - * GtkCellEditable: - * - * Interface for widgets that can be used for editing cells - * - * The `GtkCellEditable` interface must be implemented for widgets to be usable - * to edit the contents of a `GtkTreeView` cell. It provides a way to specify how - * temporary widgets should be configured for editing, get the new value, etc. - */ - -#include "config.h" -#include "gtkcelleditable.h" -#include "gtkmarshalers.h" -#include "gtkprivate.h" - - -typedef GtkCellEditableIface GtkCellEditableInterface; -G_DEFINE_INTERFACE(GtkCellEditable, gtk_cell_editable, GTK_TYPE_WIDGET) - -static void -gtk_cell_editable_default_init (GtkCellEditableInterface *iface) -{ - /** - * GtkCellEditable:editing-canceled: - * - * Indicates whether editing on the cell has been canceled. - */ - g_object_interface_install_property (iface, - g_param_spec_boolean ("editing-canceled", NULL, NULL, - FALSE, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - /** - * GtkCellEditable::editing-done: - * @cell_editable: the object on which the signal was emitted - * - * This signal is a sign for the cell renderer to update its - * value from the @cell_editable. - * - * Implementations of `GtkCellEditable` are responsible for - * emitting this signal when they are done editing, e.g. - * `GtkEntry` emits this signal when the user presses Enter. Typical things to - * do in a handler for ::editing-done are to capture the edited value, - * disconnect the @cell_editable from signals on the `GtkCellRenderer`, etc. - * - * gtk_cell_editable_editing_done() is a convenience method - * for emitting `GtkCellEditable::editing-done`. - */ - g_signal_new (I_("editing-done"), - GTK_TYPE_CELL_EDITABLE, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkCellEditableIface, editing_done), - NULL, NULL, - NULL, - G_TYPE_NONE, 0); - - /** - * GtkCellEditable::remove-widget: - * @cell_editable: the object on which the signal was emitted - * - * This signal is meant to indicate that the cell is finished - * editing, and the @cell_editable widget is being removed and may - * subsequently be destroyed. - * - * Implementations of `GtkCellEditable` are responsible for - * emitting this signal when they are done editing. It must - * be emitted after the `GtkCellEditable::editing-done` signal, - * to give the cell renderer a chance to update the cell's value - * before the widget is removed. - * - * gtk_cell_editable_remove_widget() is a convenience method - * for emitting `GtkCellEditable::remove-widget`. - */ - g_signal_new (I_("remove-widget"), - GTK_TYPE_CELL_EDITABLE, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkCellEditableIface, remove_widget), - NULL, NULL, - NULL, - G_TYPE_NONE, 0); -} - -/** - * gtk_cell_editable_start_editing: - * @cell_editable: A `GtkCellEditable` - * @event: (nullable): The `GdkEvent` that began the editing process, or - * %NULL if editing was initiated programmatically - * - * Begins editing on a @cell_editable. - * - * The `GtkCellRenderer` for the cell creates and returns a `GtkCellEditable` from - * gtk_cell_renderer_start_editing(), configured for the `GtkCellRenderer` type. - * - * gtk_cell_editable_start_editing() can then set up @cell_editable suitably for - * editing a cell, e.g. making the Esc key emit `GtkCellEditable::editing-done`. - * - * Note that the @cell_editable is created on-demand for the current edit; its - * lifetime is temporary and does not persist across other edits and/or cells. - **/ -void -gtk_cell_editable_start_editing (GtkCellEditable *cell_editable, - GdkEvent *event) -{ - g_return_if_fail (GTK_IS_CELL_EDITABLE (cell_editable)); - - (* GTK_CELL_EDITABLE_GET_IFACE (cell_editable)->start_editing) (cell_editable, event); -} - -/** - * gtk_cell_editable_editing_done: - * @cell_editable: A `GtkCellEditable` - * - * Emits the `GtkCellEditable::editing-done` signal. - */ -void -gtk_cell_editable_editing_done (GtkCellEditable *cell_editable) -{ - g_return_if_fail (GTK_IS_CELL_EDITABLE (cell_editable)); - - g_signal_emit_by_name (cell_editable, "editing-done"); -} - -/** - * gtk_cell_editable_remove_widget: - * @cell_editable: A `GtkCellEditable` - * - * Emits the `GtkCellEditable::remove-widget` signal. - **/ -void -gtk_cell_editable_remove_widget (GtkCellEditable *cell_editable) -{ - g_return_if_fail (GTK_IS_CELL_EDITABLE (cell_editable)); - - g_signal_emit_by_name (cell_editable, "remove-widget"); -} diff --git a/gtk/gtkcelleditable.h b/gtk/gtkcelleditable.h deleted file mode 100644 index be60227448..0000000000 --- a/gtk/gtkcelleditable.h +++ /dev/null @@ -1,77 +0,0 @@ -/* gtkcelleditable.h - * Copyright (C) 2001 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#ifndef __GTK_CELL_EDITABLE_H__ -#define __GTK_CELL_EDITABLE_H__ - -#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) -#error "Only can be included directly." -#endif - -#include - -G_BEGIN_DECLS - -#define GTK_TYPE_CELL_EDITABLE (gtk_cell_editable_get_type ()) -#define GTK_CELL_EDITABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_EDITABLE, GtkCellEditable)) -#define GTK_IS_CELL_EDITABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_EDITABLE)) -#define GTK_CELL_EDITABLE_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GTK_TYPE_CELL_EDITABLE, GtkCellEditableIface)) - -typedef struct _GtkCellEditable GtkCellEditable; /* Dummy typedef */ -typedef struct _GtkCellEditableIface GtkCellEditableIface; - -/** - * GtkCellEditableIface: - * @editing_done: Signal is a sign for the cell renderer to update its - * value from the cell_editable. - * @remove_widget: Signal is meant to indicate that the cell is - * finished editing, and the widget may now be destroyed. - * @start_editing: Begins editing on a cell_editable. - */ -struct _GtkCellEditableIface -{ - /*< private >*/ - GTypeInterface g_iface; - - /*< public >*/ - - /* signals */ - void (* editing_done) (GtkCellEditable *cell_editable); - void (* remove_widget) (GtkCellEditable *cell_editable); - - /* virtual table */ - void (* start_editing) (GtkCellEditable *cell_editable, - GdkEvent *event); -}; - - -GDK_AVAILABLE_IN_ALL -GType gtk_cell_editable_get_type (void) G_GNUC_CONST; - -GDK_AVAILABLE_IN_ALL -void gtk_cell_editable_start_editing (GtkCellEditable *cell_editable, - GdkEvent *event); -GDK_AVAILABLE_IN_ALL -void gtk_cell_editable_editing_done (GtkCellEditable *cell_editable); -GDK_AVAILABLE_IN_ALL -void gtk_cell_editable_remove_widget (GtkCellEditable *cell_editable); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkCellEditable, g_object_unref) - -G_END_DECLS - -#endif /* __GTK_CELL_EDITABLE_H__ */ diff --git a/gtk/gtkcelllayout.c b/gtk/gtkcelllayout.c deleted file mode 100644 index 158c20b240..0000000000 --- a/gtk/gtkcelllayout.c +++ /dev/null @@ -1,961 +0,0 @@ -/* gtkcelllayout.c - * Copyright (C) 2003 Kristian Rietveld - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -/** - * GtkCellLayout: - * - * An interface for packing cells - * - * `GtkCellLayout` is an interface to be implemented by all objects which - * want to provide a `GtkTreeViewColumn` like API for packing cells, - * setting attributes and data funcs. - * - * One of the notable features provided by implementations of - * `GtkCellLayout` are attributes. Attributes let you set the properties - * in flexible ways. They can just be set to constant values like regular - * properties. But they can also be mapped to a column of the underlying - * tree model with gtk_cell_layout_set_attributes(), which means that the value - * of the attribute can change from cell to cell as they are rendered by - * the cell renderer. Finally, it is possible to specify a function with - * gtk_cell_layout_set_cell_data_func() that is called to determine the - * value of the attribute for each cell that is rendered. - * - * # GtkCellLayouts as GtkBuildable - * - * Implementations of GtkCellLayout which also implement the GtkBuildable - * interface (`GtkCellView`, `GtkIconView`, `GtkComboBox`, - * `GtkEntryCompletion`, `GtkTreeViewColumn`) accept `GtkCellRenderer` objects - * as `` elements in UI definitions. They support a custom `` - * element for their children, which can contain multiple `` - * elements. Each `` element has a name attribute which specifies - * a property of the cell renderer; the content of the element is the - * attribute value. - * - * This is an example of a UI definition fragment specifying attributes: - * - * ```xml - * - * - * - * - * 0 - * - * - * - * ``` - * - * Furthermore for implementations of `GtkCellLayout` that use a `GtkCellArea` - * to lay out cells (all `GtkCellLayout`s in GTK use a `GtkCellArea`) - * [cell properties](class.CellArea.html#cell-properties) can also be defined - * in the format by specifying the custom `` attribute which can - * contain multiple `` elements. - * - * Here is a UI definition fragment specifying cell properties: - * - * ```xml - * - * - * - * - * True - * False - * - * - * - * ``` - * - * # Subclassing GtkCellLayout implementations - * - * When subclassing a widget that implements `GtkCellLayout` like - * `GtkIconView` or `GtkComboBox`, there are some considerations related - * to the fact that these widgets internally use a `GtkCellArea`. - * The cell area is exposed as a construct-only property by these - * widgets. This means that it is possible to e.g. do - * - * ```c - * GtkWIdget *combo = - * g_object_new (GTK_TYPE_COMBO_BOX, "cell-area", my_cell_area, NULL); - * ``` - * - * to use a custom cell area with a combo box. But construct properties - * are only initialized after instance `init()` - * functions have run, which means that using functions which rely on - * the existence of the cell area in your subclass `init()` function will - * cause the default cell area to be instantiated. In this case, a provided - * construct property value will be ignored (with a warning, to alert - * you to the problem). - * - * ```c - * static void - * my_combo_box_init (MyComboBox *b) - * { - * GtkCellRenderer *cell; - * - * cell = gtk_cell_renderer_pixbuf_new (); - * - * // The following call causes the default cell area for combo boxes, - * // a GtkCellAreaBox, to be instantiated - * gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (b), cell, FALSE); - * ... - * } - * - * GtkWidget * - * my_combo_box_new (GtkCellArea *area) - * { - * // This call is going to cause a warning about area being ignored - * return g_object_new (MY_TYPE_COMBO_BOX, "cell-area", area, NULL); - * } - * ``` - * - * If supporting alternative cell areas with your derived widget is - * not important, then this does not have to concern you. If you want - * to support alternative cell areas, you can do so by moving the - * problematic calls out of `init()` and into a `constructor()` - * for your class. - */ - -#include "config.h" -#include -#include -#include -#include "gtkcelllayout.h" -#include "gtkbuilderprivate.h" - -#define warn_no_cell_area(func) \ - g_critical ("%s: Called but no GtkCellArea is available yet", func) - -typedef GtkCellLayoutIface GtkCellLayoutInterface; -G_DEFINE_INTERFACE (GtkCellLayout, gtk_cell_layout, G_TYPE_OBJECT); - -static void gtk_cell_layout_default_pack_start (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - gboolean expand); -static void gtk_cell_layout_default_pack_end (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - gboolean expand); -static void gtk_cell_layout_default_clear (GtkCellLayout *cell_layout); -static void gtk_cell_layout_default_add_attribute (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - const char *attribute, - int column); -static void gtk_cell_layout_default_set_cell_data_func (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - GtkCellLayoutDataFunc func, - gpointer func_data, - GDestroyNotify destroy); -static void gtk_cell_layout_default_clear_attributes (GtkCellLayout *cell_layout, - GtkCellRenderer *cell); -static void gtk_cell_layout_default_reorder (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - int position); -static GList *gtk_cell_layout_default_get_cells (GtkCellLayout *cell_layout); - - -static void -gtk_cell_layout_default_init (GtkCellLayoutIface *iface) -{ - iface->pack_start = gtk_cell_layout_default_pack_start; - iface->pack_end = gtk_cell_layout_default_pack_end; - iface->clear = gtk_cell_layout_default_clear; - iface->add_attribute = gtk_cell_layout_default_add_attribute; - iface->set_cell_data_func = gtk_cell_layout_default_set_cell_data_func; - iface->clear_attributes = gtk_cell_layout_default_clear_attributes; - iface->reorder = gtk_cell_layout_default_reorder; - iface->get_cells = gtk_cell_layout_default_get_cells; -} - -/* Default implementation is to fall back on an underlying cell area */ -static void -gtk_cell_layout_default_pack_start (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - gboolean expand) -{ - GtkCellLayoutIface *iface; - GtkCellArea *area; - - iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout); - - if (iface->get_area) - { - area = iface->get_area (cell_layout); - - if (area) - gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (area), cell, expand); - else - warn_no_cell_area ("GtkCellLayoutIface->pack_start()"); - } -} - -static void -gtk_cell_layout_default_pack_end (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - gboolean expand) -{ - GtkCellLayoutIface *iface; - GtkCellArea *area; - - iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout); - - if (iface->get_area) - { - area = iface->get_area (cell_layout); - - if (area) - gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (area), cell, expand); - else - warn_no_cell_area ("GtkCellLayoutIface->pack_end()"); - } -} - -static void -gtk_cell_layout_default_clear (GtkCellLayout *cell_layout) -{ - GtkCellLayoutIface *iface; - GtkCellArea *area; - - iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout); - - if (iface->get_area) - { - area = iface->get_area (cell_layout); - - if (area) - gtk_cell_layout_clear (GTK_CELL_LAYOUT (area)); - else - warn_no_cell_area ("GtkCellLayoutIface->clear()"); - } -} - -static void -gtk_cell_layout_default_add_attribute (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - const char *attribute, - int column) -{ - GtkCellLayoutIface *iface; - GtkCellArea *area; - - iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout); - - if (iface->get_area) - { - area = iface->get_area (cell_layout); - - if (area) - gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (area), cell, attribute, column); - else - warn_no_cell_area ("GtkCellLayoutIface->add_attribute()"); - } -} - -static void -gtk_cell_layout_default_set_cell_data_func (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - GtkCellLayoutDataFunc func, - gpointer func_data, - GDestroyNotify destroy) -{ - GtkCellLayoutIface *iface; - GtkCellArea *area; - - iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout); - - if (iface->get_area) - { - area = iface->get_area (cell_layout); - - if (area) - _gtk_cell_area_set_cell_data_func_with_proxy (area, cell, - (GFunc)func, func_data, destroy, - cell_layout); - else - warn_no_cell_area ("GtkCellLayoutIface->set_cell_data_func()"); - } -} - -static void -gtk_cell_layout_default_clear_attributes (GtkCellLayout *cell_layout, - GtkCellRenderer *cell) -{ - GtkCellLayoutIface *iface; - GtkCellArea *area; - - iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout); - - if (iface->get_area) - { - area = iface->get_area (cell_layout); - - if (area) - gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (area), cell); - else - warn_no_cell_area ("GtkCellLayoutIface->clear_attributes()"); - } -} - -static void -gtk_cell_layout_default_reorder (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - int position) -{ - GtkCellLayoutIface *iface; - GtkCellArea *area; - - iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout); - - if (iface->get_area) - { - area = iface->get_area (cell_layout); - - if (area) - gtk_cell_layout_reorder (GTK_CELL_LAYOUT (area), cell, position); - else - warn_no_cell_area ("GtkCellLayoutIface->reorder()"); - } -} - -static GList * -gtk_cell_layout_default_get_cells (GtkCellLayout *cell_layout) -{ - GtkCellLayoutIface *iface; - GtkCellArea *area; - - iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout); - - if (iface->get_area) - { - area = iface->get_area (cell_layout); - - if (area) - return gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (area)); - else - warn_no_cell_area ("GtkCellLayoutIface->get_cells()"); - } - return NULL; -} - - -/** - * gtk_cell_layout_pack_start: - * @cell_layout: a `GtkCellLayout` - * @cell: a `GtkCellRenderer` - * @expand: %TRUE if @cell is to be given extra space allocated to @cell_layout - * - * Packs the @cell into the beginning of @cell_layout. If @expand is %FALSE, - * then the @cell is allocated no more space than it needs. Any unused space - * is divided evenly between cells for which @expand is %TRUE. - * - * Note that reusing the same cell renderer is not supported. - */ -void -gtk_cell_layout_pack_start (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - gboolean expand) -{ - g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout)); - g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); - - GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->pack_start (cell_layout, cell, expand); -} - -/** - * gtk_cell_layout_pack_end: - * @cell_layout: a `GtkCellLayout` - * @cell: a `GtkCellRenderer` - * @expand: %TRUE if @cell is to be given extra space allocated to @cell_layout - * - * Adds the @cell to the end of @cell_layout. If @expand is %FALSE, then the - * @cell is allocated no more space than it needs. Any unused space is - * divided evenly between cells for which @expand is %TRUE. - * - * Note that reusing the same cell renderer is not supported. - */ -void -gtk_cell_layout_pack_end (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - gboolean expand) -{ - g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout)); - g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); - - GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->pack_end (cell_layout, cell, expand); -} - -/** - * gtk_cell_layout_clear: - * @cell_layout: a `GtkCellLayout` - * - * Unsets all the mappings on all renderers on @cell_layout and - * removes all renderers from @cell_layout. - */ -void -gtk_cell_layout_clear (GtkCellLayout *cell_layout) -{ - g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout)); - - GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->clear (cell_layout); -} - -static void -gtk_cell_layout_set_attributesv (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - va_list args) -{ - char *attribute; - int column; - - attribute = va_arg (args, char *); - - gtk_cell_layout_clear_attributes (cell_layout, cell); - - while (attribute != NULL) - { - column = va_arg (args, int); - - gtk_cell_layout_add_attribute (cell_layout, cell, attribute, column); - - attribute = va_arg (args, char *); - } -} - -/** - * gtk_cell_layout_set_attributes: - * @cell_layout: a `GtkCellLayout` - * @cell: a `GtkCellRenderer` - * @...: a %NULL-terminated list of attributes - * - * Sets the attributes in the parameter list as the attributes - * of @cell_layout. - * - * See [method@Gtk.CellLayout.add_attribute] for more details. - * - * The attributes should be in attribute/column order, as in - * gtk_cell_layout_add_attribute(). All existing attributes are - * removed, and replaced with the new attributes. - */ -void -gtk_cell_layout_set_attributes (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - ...) -{ - va_list args; - - g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout)); - g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); - - va_start (args, cell); - gtk_cell_layout_set_attributesv (cell_layout, cell, args); - va_end (args); -} - -/** - * gtk_cell_layout_add_attribute: - * @cell_layout: a `GtkCellLayout` - * @cell: a `GtkCellRenderer` - * @attribute: a property on the renderer - * @column: the column position on the model to get the attribute from - * - * Adds an attribute mapping to the list in @cell_layout. - * - * The @column is the column of the model to get a value from, and the - * @attribute is the property on @cell to be set from that value. So for - * example if column 2 of the model contains strings, you could have the - * “text” attribute of a `GtkCellRendererText` get its values from column 2. - * In this context "attribute" and "property" are used interchangeably. - */ -void -gtk_cell_layout_add_attribute (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - const char *attribute, - int column) -{ - g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout)); - g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); - g_return_if_fail (attribute != NULL); - g_return_if_fail (column >= 0); - - GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->add_attribute (cell_layout, cell, attribute, column); -} - -/** - * gtk_cell_layout_set_cell_data_func: - * @cell_layout: a `GtkCellLayout` - * @cell: a `GtkCellRenderer` - * @func: (nullable): the `GtkCellLayout`DataFunc to use - * @func_data: (closure): user data for @func - * @destroy: destroy notify for @func_data - * - * Sets the `GtkCellLayout`DataFunc to use for @cell_layout. - * - * This function is used instead of the standard attributes mapping - * for setting the column value, and should set the value of @cell_layout’s - * cell renderer(s) as appropriate. - * - * @func may be %NULL to remove a previously set function. - */ -void -gtk_cell_layout_set_cell_data_func (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - GtkCellLayoutDataFunc func, - gpointer func_data, - GDestroyNotify destroy) -{ - g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout)); - g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); - - GTK_CELL_LAYOUT_GET_IFACE - (cell_layout)->set_cell_data_func (cell_layout, cell, func, func_data, destroy); -} - -/** - * gtk_cell_layout_clear_attributes: - * @cell_layout: a `GtkCellLayout` - * @cell: a `GtkCellRenderer` to clear the attribute mapping on - * - * Clears all existing attributes previously set with - * gtk_cell_layout_set_attributes(). - */ -void -gtk_cell_layout_clear_attributes (GtkCellLayout *cell_layout, - GtkCellRenderer *cell) -{ - g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout)); - g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); - - GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->clear_attributes (cell_layout, cell); -} - -/** - * gtk_cell_layout_reorder: - * @cell_layout: a `GtkCellLayout` - * @cell: a `GtkCellRenderer` to reorder - * @position: new position to insert @cell at - * - * Re-inserts @cell at @position. - * - * Note that @cell has already to be packed into @cell_layout - * for this to function properly. - */ -void -gtk_cell_layout_reorder (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - int position) -{ - g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout)); - g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); - - GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->reorder (cell_layout, cell, position); -} - -/** - * gtk_cell_layout_get_cells: - * @cell_layout: a `GtkCellLayout` - * - * Returns the cell renderers which have been added to @cell_layout. - * - * Returns: (element-type GtkCellRenderer) (transfer container): - * a list of cell renderers. The list, but not the renderers has - * been newly allocated and should be freed with g_list_free() - * when no longer needed. - */ -GList * -gtk_cell_layout_get_cells (GtkCellLayout *cell_layout) -{ - g_return_val_if_fail (GTK_IS_CELL_LAYOUT (cell_layout), NULL); - - return GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->get_cells (cell_layout); -} - -/** - * gtk_cell_layout_get_area: - * @cell_layout: a `GtkCellLayout` - * - * Returns the underlying `GtkCellArea` which might be @cell_layout - * if called on a `GtkCellArea` or might be %NULL if no `GtkCellArea` - * is used by @cell_layout. - * - * Returns: (transfer none) (nullable): the cell area used by @cell_layout - */ -GtkCellArea * -gtk_cell_layout_get_area (GtkCellLayout *cell_layout) -{ - GtkCellLayoutIface *iface; - - g_return_val_if_fail (GTK_IS_CELL_LAYOUT (cell_layout), NULL); - - iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout); - if (iface->get_area) - return iface->get_area (cell_layout); - - return NULL; -} - -/* Attribute parsing */ -typedef struct { - GtkCellLayout *cell_layout; - GtkCellRenderer *renderer; - GtkBuilder *builder; - char *attr_name; - GString *string; -} AttributesSubParserData; - -static void -attributes_start_element (GtkBuildableParseContext *context, - const char *element_name, - const char **names, - const char **values, - gpointer user_data, - GError **error) -{ - AttributesSubParserData *data = (AttributesSubParserData*)user_data; - - if (strcmp (element_name, "attribute") == 0) - { - const char *name; - - if (!_gtk_builder_check_parent (data->builder, context, "attributes", error)) - return; - - if (!g_markup_collect_attributes (element_name, names, values, error, - G_MARKUP_COLLECT_STRING, "name", &name, - G_MARKUP_COLLECT_INVALID)) - { - _gtk_builder_prefix_error (data->builder, context, error); - return; - } - - data->attr_name = g_strdup (name); - } - else if (strcmp (element_name, "attributes") == 0) - { - if (!_gtk_builder_check_parent (data->builder, context, "child", error)) - return; - - if (!g_markup_collect_attributes (element_name, names, values, error, - G_MARKUP_COLLECT_INVALID, NULL, NULL, - G_MARKUP_COLLECT_INVALID)) - _gtk_builder_prefix_error (data->builder, context, error); - } - else - { - _gtk_builder_error_unhandled_tag (data->builder, context, - "GtkCellLayout", element_name, - error); - } -} - -static void -attributes_text_element (GtkBuildableParseContext *context, - const char *text, - gsize text_len, - gpointer user_data, - GError **error) -{ - AttributesSubParserData *data = (AttributesSubParserData*)user_data; - - if (data->attr_name) - g_string_append_len (data->string, text, text_len); -} - -static void -attributes_end_element (GtkBuildableParseContext *context, - const char *element_name, - gpointer user_data, - GError **error) -{ - AttributesSubParserData *data = (AttributesSubParserData*)user_data; - GValue val = G_VALUE_INIT; - - if (!data->attr_name) - return; - - if (!gtk_builder_value_from_string_type (data->builder, G_TYPE_INT, data->string->str, &val, error)) - { - _gtk_builder_prefix_error (data->builder, context, error); - return; - } - - gtk_cell_layout_add_attribute (data->cell_layout, - data->renderer, - data->attr_name, - g_value_get_int (&val)); - - g_free (data->attr_name); - data->attr_name = NULL; - - g_string_set_size (data->string, 0); -} - -static const GtkBuildableParser attributes_parser = - { - attributes_start_element, - attributes_end_element, - attributes_text_element - }; - - -/* Cell packing parsing */ -static void -gtk_cell_layout_buildable_set_cell_property (GtkCellArea *area, - GtkBuilder *builder, - GtkCellRenderer *cell, - char *name, - const char *value) -{ - GParamSpec *pspec; - GValue gvalue = G_VALUE_INIT; - GError *error = NULL; - - pspec = gtk_cell_area_class_find_cell_property (GTK_CELL_AREA_GET_CLASS (area), name); - if (!pspec) - { - g_warning ("%s does not have a property called %s", - g_type_name (G_OBJECT_TYPE (area)), name); - return; - } - - if (!gtk_builder_value_from_string (builder, pspec, value, &gvalue, &error)) - { - g_warning ("Could not read property %s:%s with value %s of type %s: %s", - g_type_name (G_OBJECT_TYPE (area)), - name, - value, - g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)), - error->message); - g_error_free (error); - return; - } - - gtk_cell_area_cell_set_property (area, cell, name, &gvalue); - g_value_unset (&gvalue); -} - -typedef struct { - GtkBuilder *builder; - GtkCellLayout *cell_layout; - GtkCellRenderer *renderer; - GString *string; - char *cell_prop_name; - char *context; - gboolean translatable; -} CellPackingSubParserData; - -static void -cell_packing_start_element (GtkBuildableParseContext *context, - const char *element_name, - const char **names, - const char **values, - gpointer user_data, - GError **error) -{ - CellPackingSubParserData *data = (CellPackingSubParserData*)user_data; - - if (strcmp (element_name, "property") == 0) - { - const char *name; - gboolean translatable = FALSE; - const char *ctx = NULL; - - if (!_gtk_builder_check_parent (data->builder, context, "cell-packing", error)) - return; - - if (!g_markup_collect_attributes (element_name, names, values, error, - G_MARKUP_COLLECT_STRING, "name", &name, - G_MARKUP_COLLECT_BOOLEAN|G_MARKUP_COLLECT_OPTIONAL, "translatable", &translatable, - G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "comments", NULL, - G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "context", &ctx, - G_MARKUP_COLLECT_INVALID)) - { - _gtk_builder_prefix_error (data->builder, context, error); - return; - } - - data->cell_prop_name = g_strdup (name); - data->translatable = translatable; - data->context = g_strdup (ctx); - } - else if (strcmp (element_name, "cell-packing") == 0) - { - if (!_gtk_builder_check_parent (data->builder, context, "child", error)) - return; - - if (!g_markup_collect_attributes (element_name, names, values, error, - G_MARKUP_COLLECT_INVALID, NULL, NULL, - G_MARKUP_COLLECT_INVALID)) - _gtk_builder_prefix_error (data->builder, context, error); - } - else - { - _gtk_builder_error_unhandled_tag (data->builder, context, - "GtkCellLayout", element_name, - error); - } -} - -static void -cell_packing_text_element (GtkBuildableParseContext *context, - const char *text, - gsize text_len, - gpointer user_data, - GError **error) -{ - CellPackingSubParserData *data = (CellPackingSubParserData*)user_data; - - if (data->cell_prop_name) - g_string_append_len (data->string, text, text_len); -} - -static void -cell_packing_end_element (GtkBuildableParseContext *context, - const char *element_name, - gpointer user_data, - GError **error) -{ - CellPackingSubParserData *data = (CellPackingSubParserData*)user_data; - GtkCellArea *area; - - area = gtk_cell_layout_get_area (data->cell_layout); - - if (area) - { - /* translate the string */ - if (data->string->len && data->translatable) - { - const char *translated; - const char * domain; - - domain = gtk_builder_get_translation_domain (data->builder); - - translated = _gtk_builder_parser_translate (domain, - data->context, - data->string->str); - g_string_assign (data->string, translated); - } - - if (data->cell_prop_name) - gtk_cell_layout_buildable_set_cell_property (area, - data->builder, - data->renderer, - data->cell_prop_name, - data->string->str); - } - else - g_warning ("%s does not have an internal GtkCellArea class and cannot apply child cell properties", - g_type_name (G_OBJECT_TYPE (data->cell_layout))); - - g_string_set_size (data->string, 0); - g_free (data->cell_prop_name); - g_free (data->context); - data->cell_prop_name = NULL; - data->context = NULL; - data->translatable = FALSE; -} - - -static const GtkBuildableParser cell_packing_parser = - { - cell_packing_start_element, - cell_packing_end_element, - cell_packing_text_element - }; - -gboolean -_gtk_cell_layout_buildable_custom_tag_start (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const char *tagname, - GtkBuildableParser *parser, - gpointer *data) -{ - AttributesSubParserData *attr_data; - CellPackingSubParserData *packing_data; - - if (!child) - return FALSE; - - if (strcmp (tagname, "attributes") == 0) - { - attr_data = g_slice_new0 (AttributesSubParserData); - attr_data->cell_layout = GTK_CELL_LAYOUT (buildable); - attr_data->renderer = GTK_CELL_RENDERER (child); - attr_data->builder = builder; - attr_data->attr_name = NULL; - attr_data->string = g_string_new (""); - - *parser = attributes_parser; - *data = attr_data; - - return TRUE; - } - else if (strcmp (tagname, "cell-packing") == 0) - { - packing_data = g_slice_new0 (CellPackingSubParserData); - packing_data->string = g_string_new (""); - packing_data->builder = builder; - packing_data->cell_layout = GTK_CELL_LAYOUT (buildable); - packing_data->renderer = GTK_CELL_RENDERER (child); - - *parser = cell_packing_parser; - *data = packing_data; - - return TRUE; - } - - return FALSE; -} - -gboolean -_gtk_cell_layout_buildable_custom_tag_end (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const char *tagname, - gpointer *data) -{ - AttributesSubParserData *attr_data; - CellPackingSubParserData *packing_data; - - if (strcmp (tagname, "attributes") == 0) - { - attr_data = (AttributesSubParserData*)data; - g_assert (!attr_data->attr_name); - g_string_free (attr_data->string, TRUE); - g_slice_free (AttributesSubParserData, attr_data); - - return TRUE; - } - else if (strcmp (tagname, "cell-packing") == 0) - { - packing_data = (CellPackingSubParserData *)data; - g_string_free (packing_data->string, TRUE); - g_slice_free (CellPackingSubParserData, packing_data); - - return TRUE; - } - return FALSE; -} - -void -_gtk_cell_layout_buildable_add_child (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const char *type) -{ - g_return_if_fail (GTK_IS_CELL_LAYOUT (buildable)); - g_return_if_fail (GTK_IS_CELL_RENDERER (child)); - - gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (buildable), GTK_CELL_RENDERER (child), FALSE); -} diff --git a/gtk/gtkcelllayout.h b/gtk/gtkcelllayout.h deleted file mode 100644 index 7ef60f6c49..0000000000 --- a/gtk/gtkcelllayout.h +++ /dev/null @@ -1,170 +0,0 @@ -/* gtkcelllayout.h - * Copyright (C) 2003 Kristian Rietveld - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#ifndef __GTK_CELL_LAYOUT_H__ -#define __GTK_CELL_LAYOUT_H__ - -#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) -#error "Only can be included directly." -#endif - -#include -#include -#include -#include - -G_BEGIN_DECLS - -#define GTK_TYPE_CELL_LAYOUT (gtk_cell_layout_get_type ()) -#define GTK_CELL_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_LAYOUT, GtkCellLayout)) -#define GTK_IS_CELL_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_LAYOUT)) -#define GTK_CELL_LAYOUT_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GTK_TYPE_CELL_LAYOUT, GtkCellLayoutIface)) - -typedef struct _GtkCellLayout GtkCellLayout; /* dummy typedef */ -typedef struct _GtkCellLayoutIface GtkCellLayoutIface; - -/* keep in sync with GtkTreeCellDataFunc */ -/** - * GtkCellLayoutDataFunc: - * @cell_layout: a `GtkCellLayout` - * @cell: the cell renderer whose value is to be set - * @tree_model: the model - * @iter: a `GtkTreeIter` indicating the row to set the value for - * @data: (closure): user data passed to gtk_cell_layout_set_cell_data_func() - * - * A function which should set the value of @cell_layout’s cell renderer(s) - * as appropriate. - */ -typedef void (* GtkCellLayoutDataFunc) (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - GtkTreeModel *tree_model, - GtkTreeIter *iter, - gpointer data); - -/** - * GtkCellLayoutIface: - * @pack_start: Packs the cell into the beginning of cell_layout. - * @pack_end: Adds the cell to the end of cell_layout. - * @clear: Unsets all the mappings on all renderers on cell_layout and - * removes all renderers from cell_layout. - * @add_attribute: Adds an attribute mapping to the list in - * cell_layout. - * @set_cell_data_func: Sets the `GtkCellLayout`DataFunc to use for - * cell_layout. - * @clear_attributes: Clears all existing attributes previously set - * with gtk_cell_layout_set_attributes(). - * @reorder: Re-inserts cell at position. - * @get_cells: Get the cell renderers which have been added to - * cell_layout. - * @get_area: Get the underlying `GtkCellArea` which might be - * cell_layout if called on a `GtkCellArea` or might be NULL if no - * `GtkCellArea` is used by cell_layout. - */ -struct _GtkCellLayoutIface -{ - /*< private >*/ - GTypeInterface g_iface; - - /*< public >*/ - - /* Virtual Table */ - void (* pack_start) (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - gboolean expand); - void (* pack_end) (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - gboolean expand); - void (* clear) (GtkCellLayout *cell_layout); - void (* add_attribute) (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - const char *attribute, - int column); - void (* set_cell_data_func) (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - GtkCellLayoutDataFunc func, - gpointer func_data, - GDestroyNotify destroy); - void (* clear_attributes) (GtkCellLayout *cell_layout, - GtkCellRenderer *cell); - void (* reorder) (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - int position); - GList* (* get_cells) (GtkCellLayout *cell_layout); - - GtkCellArea *(* get_area) (GtkCellLayout *cell_layout); -}; - -GDK_AVAILABLE_IN_ALL -GType gtk_cell_layout_get_type (void) G_GNUC_CONST; -GDK_AVAILABLE_IN_ALL -void gtk_cell_layout_pack_start (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - gboolean expand); -GDK_AVAILABLE_IN_ALL -void gtk_cell_layout_pack_end (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - gboolean expand); -GDK_AVAILABLE_IN_ALL -GList *gtk_cell_layout_get_cells (GtkCellLayout *cell_layout); -GDK_AVAILABLE_IN_ALL -void gtk_cell_layout_clear (GtkCellLayout *cell_layout); -GDK_AVAILABLE_IN_ALL -void gtk_cell_layout_set_attributes (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - ...) G_GNUC_NULL_TERMINATED; -GDK_AVAILABLE_IN_ALL -void gtk_cell_layout_add_attribute (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - const char *attribute, - int column); -GDK_AVAILABLE_IN_ALL -void gtk_cell_layout_set_cell_data_func (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - GtkCellLayoutDataFunc func, - gpointer func_data, - GDestroyNotify destroy); -GDK_AVAILABLE_IN_ALL -void gtk_cell_layout_clear_attributes (GtkCellLayout *cell_layout, - GtkCellRenderer *cell); -GDK_AVAILABLE_IN_ALL -void gtk_cell_layout_reorder (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - int position); -GDK_AVAILABLE_IN_ALL -GtkCellArea *gtk_cell_layout_get_area (GtkCellLayout *cell_layout); - -gboolean _gtk_cell_layout_buildable_custom_tag_start (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const char *tagname, - GtkBuildableParser *parser, - gpointer *data); -gboolean _gtk_cell_layout_buildable_custom_tag_end (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const char *tagname, - gpointer *data); -void _gtk_cell_layout_buildable_add_child (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const char *type); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkCellLayout, g_object_unref) - -G_END_DECLS - -#endif /* __GTK_CELL_LAYOUT_H__ */ diff --git a/gtk/gtkcellrenderer.c b/gtk/gtkcellrenderer.c deleted file mode 100644 index 80937d571e..0000000000 --- a/gtk/gtkcellrenderer.c +++ /dev/null @@ -1,1746 +0,0 @@ -/* gtkcellrenderer.c - * Copyright (C) 2000 Red Hat, Inc. Jonathan Blandford - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#include "config.h" - -#include "gtkcellrenderer.h" - -#include "gtkmarshalers.h" -#include "gtkprivate.h" -#include "gtksnapshot.h" -#include "gtkstylecontext.h" -#include "gtktreeprivate.h" -#include "gtktypebuiltins.h" - -/** - * GtkCellRenderer: - * - * An object for rendering a single cell - * - * The `GtkCellRenderer` is a base class of a set of objects used for - * rendering a cell to a `cairo_t`. These objects are used primarily by - * the `GtkTreeView` widget, though they aren’t tied to them in any - * specific way. It is worth noting that `GtkCellRenderer` is not a - * `GtkWidget` and cannot be treated as such. - * - * The primary use of a `GtkCellRenderer` is for drawing a certain graphical - * elements on a `cairo_t`. Typically, one cell renderer is used to - * draw many cells on the screen. To this extent, it isn’t expected that a - * CellRenderer keep any permanent state around. Instead, any state is set - * just prior to use using `GObject`s property system. Then, the - * cell is measured using gtk_cell_renderer_get_preferred_size(). Finally, the cell - * is rendered in the correct location using gtk_cell_renderer_snapshot(). - * - * There are a number of rules that must be followed when writing a new - * `GtkCellRenderer`. First and foremost, it’s important that a certain set - * of properties will always yield a cell renderer of the same size, - * barring a style change. The `GtkCellRenderer` also has a number of - * generic properties that are expected to be honored by all children. - * - * Beyond merely rendering a cell, cell renderers can optionally - * provide active user interface elements. A cell renderer can be - * “activatable” like `GtkCellRenderer`Toggle, - * which toggles when it gets activated by a mouse click, or it can be - * “editable” like `GtkCellRenderer`Text, which - * allows the user to edit the text using a widget implementing the - * `GtkCellEditable` interface, e.g. `GtkEntry`. - * To make a cell renderer activatable or editable, you have to - * implement the `GtkCellRenderer`Class.activate or - * `GtkCellRenderer`Class.start_editing virtual functions, respectively. - * - * Many properties of `GtkCellRenderer` and its subclasses have a - * corresponding “set” property, e.g. “cell-background-set” corresponds - * to “cell-background”. These “set” properties reflect whether a property - * has been set or not. You should not set them independently. - */ - - -#define DEBUG_CELL_SIZE_REQUEST 0 - -static void gtk_cell_renderer_init (GtkCellRenderer *cell); -static void gtk_cell_renderer_class_init (GtkCellRendererClass *class); -static void gtk_cell_renderer_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec); -static void gtk_cell_renderer_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec); -static void set_cell_bg_color (GtkCellRenderer *cell, - GdkRGBA *rgba); - -static GtkSizeRequestMode gtk_cell_renderer_real_get_request_mode(GtkCellRenderer *cell); -static void gtk_cell_renderer_real_get_preferred_width (GtkCellRenderer *cell, - GtkWidget *widget, - int *minimum_size, - int *natural_size); -static void gtk_cell_renderer_real_get_preferred_height (GtkCellRenderer *cell, - GtkWidget *widget, - int *minimum_size, - int *natural_size); -static void gtk_cell_renderer_real_get_preferred_height_for_width(GtkCellRenderer *cell, - GtkWidget *widget, - int width, - int *minimum_height, - int *natural_height); -static void gtk_cell_renderer_real_get_preferred_width_for_height(GtkCellRenderer *cell, - GtkWidget *widget, - int height, - int *minimum_width, - int *natural_width); -static void gtk_cell_renderer_real_get_aligned_area (GtkCellRenderer *cell, - GtkWidget *widget, - GtkCellRendererState flags, - const GdkRectangle *cell_area, - GdkRectangle *aligned_area); - - -struct _GtkCellRendererPrivate -{ - float xalign; - float yalign; - - int width; - int height; - - guint16 xpad; - guint16 ypad; - - guint mode : 2; - guint visible : 1; - guint is_expander : 1; - guint is_expanded : 1; - guint cell_background_set : 1; - guint sensitive : 1; - guint editing : 1; - - GdkRGBA cell_background; -}; - -enum { - PROP_0, - PROP_MODE, - PROP_VISIBLE, - PROP_SENSITIVE, - PROP_XALIGN, - PROP_YALIGN, - PROP_XPAD, - PROP_YPAD, - PROP_WIDTH, - PROP_HEIGHT, - PROP_IS_EXPANDER, - PROP_IS_EXPANDED, - PROP_CELL_BACKGROUND, - PROP_CELL_BACKGROUND_RGBA, - PROP_CELL_BACKGROUND_SET, - PROP_EDITING -}; - -/* Signal IDs */ -enum { - EDITING_CANCELED, - EDITING_STARTED, - LAST_SIGNAL -}; - -static int GtkCellRenderer_private_offset; -static guint cell_renderer_signals[LAST_SIGNAL] = { 0 }; - -static inline gpointer -gtk_cell_renderer_get_instance_private (GtkCellRenderer *self) -{ - return (G_STRUCT_MEMBER_P (self, GtkCellRenderer_private_offset)); -} - -static void -gtk_cell_renderer_init (GtkCellRenderer *cell) -{ - GtkCellRendererPrivate *priv; - - cell->priv = gtk_cell_renderer_get_instance_private (cell); - priv = cell->priv; - - priv->mode = GTK_CELL_RENDERER_MODE_INERT; - priv->visible = TRUE; - priv->width = -1; - priv->height = -1; - priv->xalign = 0.5; - priv->yalign = 0.5; - priv->xpad = 0; - priv->ypad = 0; - priv->sensitive = TRUE; - priv->is_expander = FALSE; - priv->is_expanded = FALSE; - priv->editing = FALSE; -} - -static void -gtk_cell_renderer_class_init (GtkCellRendererClass *class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (class); - - object_class->get_property = gtk_cell_renderer_get_property; - object_class->set_property = gtk_cell_renderer_set_property; - - class->snapshot = NULL; - class->get_request_mode = gtk_cell_renderer_real_get_request_mode; - class->get_preferred_width = gtk_cell_renderer_real_get_preferred_width; - class->get_preferred_height = gtk_cell_renderer_real_get_preferred_height; - class->get_preferred_width_for_height = gtk_cell_renderer_real_get_preferred_width_for_height; - class->get_preferred_height_for_width = gtk_cell_renderer_real_get_preferred_height_for_width; - class->get_aligned_area = gtk_cell_renderer_real_get_aligned_area; - - /** - * GtkCellRenderer::editing-canceled: - * @renderer: the object which received the signal - * - * This signal gets emitted when the user cancels the process of editing a - * cell. For example, an editable cell renderer could be written to cancel - * editing when the user presses Escape. - * - * See also: gtk_cell_renderer_stop_editing(). - */ - cell_renderer_signals[EDITING_CANCELED] = - g_signal_new (I_("editing-canceled"), - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (GtkCellRendererClass, editing_canceled), - NULL, NULL, - NULL, - G_TYPE_NONE, 0); - - /** - * GtkCellRenderer::editing-started: - * @renderer: the object which received the signal - * @editable: the `GtkCellEditable` - * @path: the path identifying the edited cell - * - * This signal gets emitted when a cell starts to be edited. - * The intended use of this signal is to do special setup - * on @editable, e.g. adding a `GtkEntryCompletion` or setting - * up additional columns in a `GtkComboBox`. - * - * See gtk_cell_editable_start_editing() for information on the lifecycle of - * the @editable and a way to do setup that doesn’t depend on the @renderer. - * - * Note that GTK doesn't guarantee that cell renderers will - * continue to use the same kind of widget for editing in future - * releases, therefore you should check the type of @editable - * before doing any specific setup, as in the following example: - * |[ - * static void - * text_editing_started (GtkCellRenderer *cell, - * GtkCellEditable *editable, - * const char *path, - * gpointer data) - * { - * if (GTK_IS_ENTRY (editable)) - * { - * GtkEntry *entry = GTK_ENTRY (editable); - * - * // ... create a GtkEntryCompletion - * - * gtk_entry_set_completion (entry, completion); - * } - * } - * ]| - */ - cell_renderer_signals[EDITING_STARTED] = - g_signal_new (I_("editing-started"), - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (GtkCellRendererClass, editing_started), - NULL, NULL, - _gtk_marshal_VOID__OBJECT_STRING, - G_TYPE_NONE, 2, - GTK_TYPE_CELL_EDITABLE, - G_TYPE_STRING); - g_signal_set_va_marshaller (cell_renderer_signals[EDITING_STARTED], - G_TYPE_FROM_CLASS (object_class), - _gtk_marshal_VOID__OBJECT_STRINGv); - - g_object_class_install_property (object_class, - PROP_MODE, - g_param_spec_enum ("mode", NULL, NULL, - GTK_TYPE_CELL_RENDERER_MODE, - GTK_CELL_RENDERER_MODE_INERT, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - g_object_class_install_property (object_class, - PROP_VISIBLE, - g_param_spec_boolean ("visible", NULL, NULL, - TRUE, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - g_object_class_install_property (object_class, - PROP_SENSITIVE, - g_param_spec_boolean ("sensitive", NULL, NULL, - TRUE, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - g_object_class_install_property (object_class, - PROP_XALIGN, - g_param_spec_float ("xalign", NULL, NULL, - 0.0, - 1.0, - 0.5, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - g_object_class_install_property (object_class, - PROP_YALIGN, - g_param_spec_float ("yalign", NULL, NULL, - 0.0, - 1.0, - 0.5, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - g_object_class_install_property (object_class, - PROP_XPAD, - g_param_spec_uint ("xpad", NULL, NULL, - 0, - G_MAXUINT, - 0, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - g_object_class_install_property (object_class, - PROP_YPAD, - g_param_spec_uint ("ypad", NULL, NULL, - 0, - G_MAXUINT, - 0, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - g_object_class_install_property (object_class, - PROP_WIDTH, - g_param_spec_int ("width", NULL, NULL, - -1, - G_MAXINT, - -1, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - g_object_class_install_property (object_class, - PROP_HEIGHT, - g_param_spec_int ("height", NULL, NULL, - -1, - G_MAXINT, - -1, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - g_object_class_install_property (object_class, - PROP_IS_EXPANDER, - g_param_spec_boolean ("is-expander", NULL, NULL, - FALSE, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - - g_object_class_install_property (object_class, - PROP_IS_EXPANDED, - g_param_spec_boolean ("is-expanded", NULL, NULL, - FALSE, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - g_object_class_install_property (object_class, - PROP_CELL_BACKGROUND, - g_param_spec_string ("cell-background", NULL, NULL, - NULL, - GTK_PARAM_WRITABLE)); - - /** - * GtkCellRenderer:cell-background-rgba: - * - * Cell background as a `GdkRGBA` - */ - g_object_class_install_property (object_class, - PROP_CELL_BACKGROUND_RGBA, - g_param_spec_boxed ("cell-background-rgba", NULL, NULL, - GDK_TYPE_RGBA, - GTK_PARAM_READWRITE)); - - g_object_class_install_property (object_class, - PROP_EDITING, - g_param_spec_boolean ("editing", NULL, NULL, - FALSE, - GTK_PARAM_READABLE)); - - -#define ADD_SET_PROP(propname, propval, nick, blurb) g_object_class_install_property (object_class, propval, g_param_spec_boolean (propname, nick, blurb, FALSE, GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)) - - ADD_SET_PROP ("cell-background-set", PROP_CELL_BACKGROUND_SET, NULL, NULL); - - if (GtkCellRenderer_private_offset != 0) - g_type_class_adjust_private_offset (class, &GtkCellRenderer_private_offset); -} - -GType -gtk_cell_renderer_get_type (void) -{ - static GType cell_renderer_type = 0; - - if (G_UNLIKELY (cell_renderer_type == 0)) - { - const GTypeInfo cell_renderer_info = - { - sizeof (GtkCellRendererClass), - NULL, - NULL, - (GClassInitFunc) gtk_cell_renderer_class_init, - NULL, /* class_finalize */ - NULL, /* class_init */ - sizeof (GtkWidget), - 0, /* n_preallocs */ - (GInstanceInitFunc) gtk_cell_renderer_init, - NULL, /* value_table */ - }; - cell_renderer_type = g_type_register_static (G_TYPE_INITIALLY_UNOWNED, "GtkCellRenderer", - &cell_renderer_info, G_TYPE_FLAG_ABSTRACT); - - GtkCellRenderer_private_offset = - g_type_add_instance_private (cell_renderer_type, sizeof (GtkCellRendererPrivate)); - } - - return cell_renderer_type; -} - -static void -gtk_cell_renderer_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec) -{ - GtkCellRenderer *cell = GTK_CELL_RENDERER (object); - GtkCellRendererPrivate *priv = cell->priv; - - switch (param_id) - { - case PROP_MODE: - g_value_set_enum (value, priv->mode); - break; - case PROP_VISIBLE: - g_value_set_boolean (value, priv->visible); - break; - case PROP_SENSITIVE: - g_value_set_boolean (value, priv->sensitive); - break; - case PROP_EDITING: - g_value_set_boolean (value, priv->editing); - break; - case PROP_XALIGN: - g_value_set_float (value, priv->xalign); - break; - case PROP_YALIGN: - g_value_set_float (value, priv->yalign); - break; - case PROP_XPAD: - g_value_set_uint (value, priv->xpad); - break; - case PROP_YPAD: - g_value_set_uint (value, priv->ypad); - break; - case PROP_WIDTH: - g_value_set_int (value, priv->width); - break; - case PROP_HEIGHT: - g_value_set_int (value, priv->height); - break; - case PROP_IS_EXPANDER: - g_value_set_boolean (value, priv->is_expander); - break; - case PROP_IS_EXPANDED: - g_value_set_boolean (value, priv->is_expanded); - break; - case PROP_CELL_BACKGROUND_RGBA: - g_value_set_boxed (value, &priv->cell_background); - break; - case PROP_CELL_BACKGROUND_SET: - g_value_set_boolean (value, priv->cell_background_set); - break; - case PROP_CELL_BACKGROUND: - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); - break; - } - -} - -static void -gtk_cell_renderer_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec) -{ - GtkCellRenderer *cell = GTK_CELL_RENDERER (object); - GtkCellRendererPrivate *priv = cell->priv; - - switch (param_id) - { - case PROP_MODE: - if (priv->mode != g_value_get_enum (value)) - { - priv->mode = g_value_get_enum (value); - g_object_notify_by_pspec (object, pspec); - } - break; - case PROP_VISIBLE: - if (priv->visible != g_value_get_boolean (value)) - { - priv->visible = g_value_get_boolean (value); - g_object_notify_by_pspec (object, pspec); - } - break; - case PROP_SENSITIVE: - if (priv->sensitive != g_value_get_boolean (value)) - { - priv->sensitive = g_value_get_boolean (value); - g_object_notify_by_pspec (object, pspec); - } - break; - case PROP_XALIGN: - if (priv->xalign != g_value_get_float (value)) - { - priv->xalign = g_value_get_float (value); - g_object_notify_by_pspec (object, pspec); - } - break; - case PROP_YALIGN: - if (priv->yalign != g_value_get_float (value)) - { - priv->yalign = g_value_get_float (value); - g_object_notify_by_pspec (object, pspec); - } - break; - case PROP_XPAD: - if (priv->xpad != g_value_get_uint (value)) - { - priv->xpad = g_value_get_uint (value); - g_object_notify_by_pspec (object, pspec); - } - break; - case PROP_YPAD: - if (priv->ypad != g_value_get_uint (value)) - { - priv->ypad = g_value_get_uint (value); - g_object_notify_by_pspec (object, pspec); - } - break; - case PROP_WIDTH: - if (priv->width != g_value_get_int (value)) - { - priv->width = g_value_get_int (value); - g_object_notify_by_pspec (object, pspec); - } - break; - case PROP_HEIGHT: - if (priv->height != g_value_get_int (value)) - { - priv->height = g_value_get_int (value); - g_object_notify_by_pspec (object, pspec); - } - break; - case PROP_IS_EXPANDER: - if (priv->is_expander != g_value_get_boolean (value)) - { - priv->is_expander = g_value_get_boolean (value); - g_object_notify_by_pspec (object, pspec); - } - break; - case PROP_IS_EXPANDED: - if (priv->is_expanded != g_value_get_boolean (value)) - { - priv->is_expanded = g_value_get_boolean (value); - g_object_notify_by_pspec (object, pspec); - } - break; - case PROP_CELL_BACKGROUND: - { - GdkRGBA rgba; - - if (!g_value_get_string (value)) - set_cell_bg_color (cell, NULL); - else if (gdk_rgba_parse (&rgba, g_value_get_string (value))) - set_cell_bg_color (cell, &rgba); - else - g_warning ("Don't know color '%s'", g_value_get_string (value)); - - g_object_notify (object, "cell-background"); - } - break; - case PROP_CELL_BACKGROUND_RGBA: - set_cell_bg_color (cell, g_value_get_boxed (value)); - break; - case PROP_CELL_BACKGROUND_SET: - if (priv->cell_background_set != g_value_get_boolean (value)) - { - priv->cell_background_set = g_value_get_boolean (value); - g_object_notify (object, "cell-background-set"); - } - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); - break; - } -} - -static void -set_cell_bg_color (GtkCellRenderer *cell, - GdkRGBA *rgba) -{ - GtkCellRendererPrivate *priv = cell->priv; - - if (rgba) - { - if (!priv->cell_background_set) - { - priv->cell_background_set = TRUE; - g_object_notify (G_OBJECT (cell), "cell-background-set"); - } - - priv->cell_background = *rgba; - } - else - { - if (priv->cell_background_set) - { - priv->cell_background_set = FALSE; - g_object_notify (G_OBJECT (cell), "cell-background-set"); - } - } - g_object_notify (G_OBJECT (cell), "cell-background-rgba"); -} - -/** - * gtk_cell_renderer_snapshot: - * @cell: a `GtkCellRenderer` - * @snapshot: a `GtkSnapshot` to draw to - * @widget: the widget owning @window - * @background_area: entire cell area (including tree expanders and maybe - * padding on the sides) - * @cell_area: area normally rendered by a cell renderer - * @flags: flags that affect rendering - * - * Invokes the virtual render function of the `GtkCellRenderer`. The three - * passed-in rectangles are areas in @cr. Most renderers will draw within - * @cell_area; the xalign, yalign, xpad, and ypad fields of the `GtkCellRenderer` - * should be honored with respect to @cell_area. @background_area includes the - * blank space around the cell, and also the area containing the tree expander; - * so the @background_area rectangles for all cells tile to cover the entire - * @window. - **/ -void -gtk_cell_renderer_snapshot (GtkCellRenderer *cell, - GtkSnapshot *snapshot, - GtkWidget *widget, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - GtkCellRendererState flags) -{ - gboolean selected = FALSE; - GtkCellRendererPrivate *priv = cell->priv; - GtkStyleContext *context; - GtkStateFlags state; - - g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); - g_return_if_fail (GTK_CELL_RENDERER_GET_CLASS (cell)->snapshot != NULL); - g_return_if_fail (snapshot != NULL); - - selected = (flags & GTK_CELL_RENDERER_SELECTED) == GTK_CELL_RENDERER_SELECTED; - - gtk_snapshot_push_debug (snapshot, "%s", G_OBJECT_TYPE_NAME (cell)); - - if (priv->cell_background_set && !selected) - { - gtk_snapshot_append_color (snapshot, - &priv->cell_background, - &GRAPHENE_RECT_INIT ( - background_area->x, background_area->y, - background_area->width, background_area->height - )); - } - - gtk_snapshot_push_clip (snapshot, - &GRAPHENE_RECT_INIT ( - background_area->x, background_area->y, - background_area->width, background_area->height - )); - - context = gtk_widget_get_style_context (widget); - - gtk_style_context_save (context); - gtk_style_context_add_class (context, "cell"); - - state = gtk_cell_renderer_get_state (cell, widget, flags); - gtk_style_context_set_state (context, state); - - GTK_CELL_RENDERER_GET_CLASS (cell)->snapshot (cell, - snapshot, - widget, - background_area, - cell_area, - flags); - gtk_style_context_restore (context); - gtk_snapshot_pop (snapshot); - gtk_snapshot_pop (snapshot); -} - -/** - * gtk_cell_renderer_activate: - * @cell: a `GtkCellRenderer` - * @event: a `GdkEvent` - * @widget: widget that received the event - * @path: widget-dependent string representation of the event location; - * e.g. for `GtkTreeView`, a string representation of `GtkTreePath` - * @background_area: background area as passed to gtk_cell_renderer_render() - * @cell_area: cell area as passed to gtk_cell_renderer_render() - * @flags: render flags - * - * Passes an activate event to the cell renderer for possible processing. - * Some cell renderers may use events; for example, `GtkCellRendererToggle` - * toggles when it gets a mouse click. - * - * Returns: %TRUE if the event was consumed/handled - **/ -gboolean -gtk_cell_renderer_activate (GtkCellRenderer *cell, - GdkEvent *event, - GtkWidget *widget, - const char *path, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - GtkCellRendererState flags) -{ - GtkCellRendererPrivate *priv; - - g_return_val_if_fail (GTK_IS_CELL_RENDERER (cell), FALSE); - - priv = cell->priv; - - if (priv->mode != GTK_CELL_RENDERER_MODE_ACTIVATABLE) - return FALSE; - - if (GTK_CELL_RENDERER_GET_CLASS (cell)->activate == NULL) - return FALSE; - - return GTK_CELL_RENDERER_GET_CLASS (cell)->activate (cell, - event, - widget, - path, - (GdkRectangle *) background_area, - (GdkRectangle *) cell_area, - flags); -} - -/** - * gtk_cell_renderer_start_editing: - * @cell: a `GtkCellRenderer` - * @event: (nullable): a `GdkEvent` - * @widget: widget that received the event - * @path: widget-dependent string representation of the event location; - * e.g. for `GtkTreeView`, a string representation of `GtkTreePath` - * @background_area: background area as passed to gtk_cell_renderer_render() - * @cell_area: cell area as passed to gtk_cell_renderer_render() - * @flags: render flags - * - * Starts editing the contents of this @cell, through a new `GtkCellEditable` - * widget created by the `GtkCellRenderer`Class.start_editing virtual function. - * - * Returns: (nullable) (transfer none): A new `GtkCellEditable` for editing this - * @cell, or %NULL if editing is not possible - **/ -GtkCellEditable * -gtk_cell_renderer_start_editing (GtkCellRenderer *cell, - GdkEvent *event, - GtkWidget *widget, - const char *path, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - GtkCellRendererState flags) - -{ - GtkCellRendererPrivate *priv; - GtkCellEditable *editable; - - g_return_val_if_fail (GTK_IS_CELL_RENDERER (cell), NULL); - - priv = cell->priv; - - if (priv->mode != GTK_CELL_RENDERER_MODE_EDITABLE) - return NULL; - - if (GTK_CELL_RENDERER_GET_CLASS (cell)->start_editing == NULL) - return NULL; - - editable = GTK_CELL_RENDERER_GET_CLASS (cell)->start_editing (cell, - event, - widget, - path, - (GdkRectangle *) background_area, - (GdkRectangle *) cell_area, - flags); - if (editable == NULL) - return NULL; - - gtk_widget_add_css_class (GTK_WIDGET (editable), "cell"); - - g_signal_emit (cell, - cell_renderer_signals[EDITING_STARTED], 0, - editable, path); - - priv->editing = TRUE; - - return editable; -} - -/** - * gtk_cell_renderer_set_fixed_size: - * @cell: A `GtkCellRenderer` - * @width: the width of the cell renderer, or -1 - * @height: the height of the cell renderer, or -1 - * - * Sets the renderer size to be explicit, independent of the properties set. - **/ -void -gtk_cell_renderer_set_fixed_size (GtkCellRenderer *cell, - int width, - int height) -{ - GtkCellRendererPrivate *priv; - - g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); - g_return_if_fail (width >= -1 && height >= -1); - - priv = cell->priv; - - if ((width != priv->width) || (height != priv->height)) - { - g_object_freeze_notify (G_OBJECT (cell)); - - if (width != priv->width) - { - priv->width = width; - g_object_notify (G_OBJECT (cell), "width"); - } - - if (height != priv->height) - { - priv->height = height; - g_object_notify (G_OBJECT (cell), "height"); - } - - g_object_thaw_notify (G_OBJECT (cell)); - } -} - -/** - * gtk_cell_renderer_get_fixed_size: - * @cell: A `GtkCellRenderer` - * @width: (out) (optional): location to fill in with the fixed width of the cell - * @height: (out) (optional): location to fill in with the fixed height of the cell - * - * Fills in @width and @height with the appropriate size of @cell. - */ -void -gtk_cell_renderer_get_fixed_size (GtkCellRenderer *cell, - int *width, - int *height) -{ - GtkCellRendererPrivate *priv; - - g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); - - priv = cell->priv; - - if (width) - *width = priv->width; - if (height) - *height = priv->height; -} - -/** - * gtk_cell_renderer_set_alignment: - * @cell: A `GtkCellRenderer` - * @xalign: the x alignment of the cell renderer - * @yalign: the y alignment of the cell renderer - * - * Sets the renderer’s alignment within its available space. - **/ -void -gtk_cell_renderer_set_alignment (GtkCellRenderer *cell, - float xalign, - float yalign) -{ - GtkCellRendererPrivate *priv; - - g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); - g_return_if_fail (xalign >= 0.0 && xalign <= 1.0); - g_return_if_fail (yalign >= 0.0 && yalign <= 1.0); - - priv = cell->priv; - - if ((xalign != priv->xalign) || (yalign != priv->yalign)) - { - g_object_freeze_notify (G_OBJECT (cell)); - - if (xalign != priv->xalign) - { - priv->xalign = xalign; - g_object_notify (G_OBJECT (cell), "xalign"); - } - - if (yalign != priv->yalign) - { - priv->yalign = yalign; - g_object_notify (G_OBJECT (cell), "yalign"); - } - - g_object_thaw_notify (G_OBJECT (cell)); - } -} - -/** - * gtk_cell_renderer_get_alignment: - * @cell: A `GtkCellRenderer` - * @xalign: (out) (optional): location to fill in with the x alignment of the cell - * @yalign: (out) (optional): location to fill in with the y alignment of the cell - * - * Fills in @xalign and @yalign with the appropriate values of @cell. - */ -void -gtk_cell_renderer_get_alignment (GtkCellRenderer *cell, - float *xalign, - float *yalign) -{ - GtkCellRendererPrivate *priv; - - g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); - - priv = cell->priv; - - if (xalign) - *xalign = priv->xalign; - if (yalign) - *yalign = priv->yalign; -} - -/** - * gtk_cell_renderer_set_padding: - * @cell: A `GtkCellRenderer` - * @xpad: the x padding of the cell renderer - * @ypad: the y padding of the cell renderer - * - * Sets the renderer’s padding. - **/ -void -gtk_cell_renderer_set_padding (GtkCellRenderer *cell, - int xpad, - int ypad) -{ - GtkCellRendererPrivate *priv; - - g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); - g_return_if_fail (xpad >= 0 && ypad >= 0); - - priv = cell->priv; - - if ((xpad != priv->xpad) || (ypad != priv->ypad)) - { - g_object_freeze_notify (G_OBJECT (cell)); - - if (xpad != priv->xpad) - { - priv->xpad = xpad; - g_object_notify (G_OBJECT (cell), "xpad"); - } - - if (ypad != priv->ypad) - { - priv->ypad = ypad; - g_object_notify (G_OBJECT (cell), "ypad"); - } - - g_object_thaw_notify (G_OBJECT (cell)); - } -} - -/** - * gtk_cell_renderer_get_padding: - * @cell: A `GtkCellRenderer` - * @xpad: (out) (optional): location to fill in with the x padding of the cell - * @ypad: (out) (optional): location to fill in with the y padding of the cell - * - * Fills in @xpad and @ypad with the appropriate values of @cell. - */ -void -gtk_cell_renderer_get_padding (GtkCellRenderer *cell, - int *xpad, - int *ypad) -{ - GtkCellRendererPrivate *priv; - - g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); - - priv = cell->priv; - - if (xpad) - *xpad = priv->xpad; - if (ypad) - *ypad = priv->ypad; -} - -/** - * gtk_cell_renderer_set_visible: - * @cell: A `GtkCellRenderer` - * @visible: the visibility of the cell - * - * Sets the cell renderer’s visibility. - **/ -void -gtk_cell_renderer_set_visible (GtkCellRenderer *cell, - gboolean visible) -{ - GtkCellRendererPrivate *priv; - - g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); - - priv = cell->priv; - - if (priv->visible != visible) - { - priv->visible = visible ? TRUE : FALSE; - g_object_notify (G_OBJECT (cell), "visible"); - } -} - -/** - * gtk_cell_renderer_get_visible: - * @cell: A `GtkCellRenderer` - * - * Returns the cell renderer’s visibility. - * - * Returns: %TRUE if the cell renderer is visible - */ -gboolean -gtk_cell_renderer_get_visible (GtkCellRenderer *cell) -{ - g_return_val_if_fail (GTK_IS_CELL_RENDERER (cell), FALSE); - - return cell->priv->visible; -} - -/** - * gtk_cell_renderer_set_sensitive: - * @cell: A `GtkCellRenderer` - * @sensitive: the sensitivity of the cell - * - * Sets the cell renderer’s sensitivity. - **/ -void -gtk_cell_renderer_set_sensitive (GtkCellRenderer *cell, - gboolean sensitive) -{ - GtkCellRendererPrivate *priv; - - g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); - - priv = cell->priv; - - if (priv->sensitive != sensitive) - { - priv->sensitive = sensitive ? TRUE : FALSE; - g_object_notify (G_OBJECT (cell), "sensitive"); - } -} - -/** - * gtk_cell_renderer_get_sensitive: - * @cell: A `GtkCellRenderer` - * - * Returns the cell renderer’s sensitivity. - * - * Returns: %TRUE if the cell renderer is sensitive - */ -gboolean -gtk_cell_renderer_get_sensitive (GtkCellRenderer *cell) -{ - g_return_val_if_fail (GTK_IS_CELL_RENDERER (cell), FALSE); - - return cell->priv->sensitive; -} - - -/** - * gtk_cell_renderer_is_activatable: - * @cell: A `GtkCellRenderer` - * - * Checks whether the cell renderer can do something when activated. - * - * Returns: %TRUE if the cell renderer can do anything when activated - */ -gboolean -gtk_cell_renderer_is_activatable (GtkCellRenderer *cell) -{ - GtkCellRendererPrivate *priv; - - g_return_val_if_fail (GTK_IS_CELL_RENDERER (cell), FALSE); - - priv = cell->priv; - - return (priv->visible && - (priv->mode == GTK_CELL_RENDERER_MODE_EDITABLE || - priv->mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE)); -} - - -/** - * gtk_cell_renderer_stop_editing: - * @cell: A `GtkCellRenderer` - * @canceled: %TRUE if the editing has been canceled - * - * Informs the cell renderer that the editing is stopped. - * If @canceled is %TRUE, the cell renderer will emit the - * `GtkCellRenderer`::editing-canceled signal. - * - * This function should be called by cell renderer implementations - * in response to the `GtkCellEditable::editing-done` signal of - * `GtkCellEditable`. - **/ -void -gtk_cell_renderer_stop_editing (GtkCellRenderer *cell, - gboolean canceled) -{ - GtkCellRendererPrivate *priv; - - g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); - - priv = cell->priv; - - if (priv->editing) - { - priv->editing = FALSE; - if (canceled) - g_signal_emit (cell, cell_renderer_signals[EDITING_CANCELED], 0); - } -} - -static void -gtk_cell_renderer_real_get_preferred_size (GtkCellRenderer *cell, - GtkWidget *widget, - GtkOrientation orientation, - int *minimum_size, - int *natural_size) -{ - GtkRequisition min_req; - - min_req.width = 0; - min_req.height = 0; - - if (orientation == GTK_ORIENTATION_HORIZONTAL) - { - if (minimum_size) - *minimum_size = min_req.width; - - if (natural_size) - *natural_size = min_req.width; - } - else - { - if (minimum_size) - *minimum_size = min_req.height; - - if (natural_size) - *natural_size = min_req.height; - } -} - -static GtkSizeRequestMode -gtk_cell_renderer_real_get_request_mode (GtkCellRenderer *cell) -{ - /* By default cell renderers are height-for-width. */ - return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH; -} - -static void -gtk_cell_renderer_real_get_preferred_width (GtkCellRenderer *cell, - GtkWidget *widget, - int *minimum_size, - int *natural_size) -{ - gtk_cell_renderer_real_get_preferred_size (cell, widget, GTK_ORIENTATION_HORIZONTAL, - minimum_size, natural_size); -} - -static void -gtk_cell_renderer_real_get_preferred_height (GtkCellRenderer *cell, - GtkWidget *widget, - int *minimum_size, - int *natural_size) -{ - gtk_cell_renderer_real_get_preferred_size (cell, widget, GTK_ORIENTATION_VERTICAL, - minimum_size, natural_size); -} - - -static void -gtk_cell_renderer_real_get_preferred_height_for_width (GtkCellRenderer *cell, - GtkWidget *widget, - int width, - int *minimum_height, - int *natural_height) -{ - gtk_cell_renderer_get_preferred_height (cell, widget, minimum_height, natural_height); -} - -static void -gtk_cell_renderer_real_get_preferred_width_for_height (GtkCellRenderer *cell, - GtkWidget *widget, - int height, - int *minimum_width, - int *natural_width) -{ - gtk_cell_renderer_get_preferred_width (cell, widget, minimum_width, natural_width); -} - - -/* Default implementation assumes that a cell renderer will never use more - * space than its natural size (this is fine for toggles and pixbufs etc - * but needs to be overridden from wrapping/ellipsizing text renderers) */ -static void -gtk_cell_renderer_real_get_aligned_area (GtkCellRenderer *cell, - GtkWidget *widget, - GtkCellRendererState flags, - const GdkRectangle *cell_area, - GdkRectangle *aligned_area) -{ - int opposite_size, x_offset, y_offset; - int natural_size; - - g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); - g_return_if_fail (GTK_IS_WIDGET (widget)); - g_return_if_fail (cell_area != NULL); - g_return_if_fail (aligned_area != NULL); - - *aligned_area = *cell_area; - - /* Trim up the aligned size */ - if (gtk_cell_renderer_get_request_mode (cell) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH) - { - gtk_cell_renderer_get_preferred_width (cell, widget, - NULL, &natural_size); - - aligned_area->width = MIN (aligned_area->width, natural_size); - - gtk_cell_renderer_get_preferred_height_for_width (cell, widget, - aligned_area->width, - NULL, &opposite_size); - - aligned_area->height = MIN (opposite_size, aligned_area->height); - } - else - { - gtk_cell_renderer_get_preferred_height (cell, widget, - NULL, &natural_size); - - aligned_area->height = MIN (aligned_area->width, natural_size); - - gtk_cell_renderer_get_preferred_width_for_height (cell, widget, - aligned_area->height, - NULL, &opposite_size); - - aligned_area->width = MIN (opposite_size, aligned_area->width); - } - - /* offset the cell position */ - _gtk_cell_renderer_calc_offset (cell, cell_area, - gtk_widget_get_direction (widget), - aligned_area->width, - aligned_area->height, - &x_offset, &y_offset); - - aligned_area->x += x_offset; - aligned_area->y += y_offset; -} - - -/* An internal convenience function for some containers to peek at the - * cell alignment in a target allocation (used to draw focus and align - * cells in the icon view). - * - * Note this is only a trivial “align * (allocation - request)” operation. - */ -void -_gtk_cell_renderer_calc_offset (GtkCellRenderer *cell, - const GdkRectangle *cell_area, - GtkTextDirection direction, - int width, - int height, - int *x_offset, - int *y_offset) -{ - GtkCellRendererPrivate *priv; - - g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); - g_return_if_fail (cell_area != NULL); - g_return_if_fail (x_offset || y_offset); - - priv = cell->priv; - - if (x_offset) - { - *x_offset = (((direction == GTK_TEXT_DIR_RTL) ? - (1.0 - priv->xalign) : priv->xalign) * - (cell_area->width - width)); - *x_offset = MAX (*x_offset, 0); - } - if (y_offset) - { - *y_offset = (priv->yalign * - (cell_area->height - height)); - *y_offset = MAX (*y_offset, 0); - } -} - -/** - * gtk_cell_renderer_get_request_mode: - * @cell: a `GtkCellRenderer` instance - * - * Gets whether the cell renderer prefers a height-for-width layout - * or a width-for-height layout. - * - * Returns: The `GtkSizeRequestMode` preferred by this renderer. - */ -GtkSizeRequestMode -gtk_cell_renderer_get_request_mode (GtkCellRenderer *cell) -{ - g_return_val_if_fail (GTK_IS_CELL_RENDERER (cell), FALSE); - - return GTK_CELL_RENDERER_GET_CLASS (cell)->get_request_mode (cell); -} - -/** - * gtk_cell_renderer_get_preferred_width: - * @cell: a `GtkCellRenderer` instance - * @widget: the `GtkWidget` this cell will be rendering to - * @minimum_size: (out) (optional): location to store the minimum size - * @natural_size: (out) (optional): location to store the natural size - * - * Retrieves a renderer’s natural size when rendered to @widget. - */ -void -gtk_cell_renderer_get_preferred_width (GtkCellRenderer *cell, - GtkWidget *widget, - int *minimum_size, - int *natural_size) -{ - GtkCellRendererClass *klass; - int width; - - g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); - g_return_if_fail (GTK_IS_WIDGET (widget)); - g_return_if_fail (NULL != minimum_size || NULL != natural_size); - - gtk_cell_renderer_get_fixed_size (GTK_CELL_RENDERER (cell), &width, NULL); - - if (width < 0) - { - klass = GTK_CELL_RENDERER_GET_CLASS (cell); - klass->get_preferred_width (cell, widget, minimum_size, natural_size); - } - else - { - if (minimum_size) - *minimum_size = width; - if (natural_size) - *natural_size = width; - } - -#if DEBUG_CELL_SIZE_REQUEST - g_message ("%s returning minimum width: %d and natural width: %d", - G_OBJECT_TYPE_NAME (cell), - minimum_size ? *minimum_size : 20000, - natural_size ? *natural_size : 20000); -#endif -} - - -/** - * gtk_cell_renderer_get_preferred_height: - * @cell: a `GtkCellRenderer` instance - * @widget: the `GtkWidget` this cell will be rendering to - * @minimum_size: (out) (optional): location to store the minimum size - * @natural_size: (out) (optional): location to store the natural size - * - * Retrieves a renderer’s natural size when rendered to @widget. - */ -void -gtk_cell_renderer_get_preferred_height (GtkCellRenderer *cell, - GtkWidget *widget, - int *minimum_size, - int *natural_size) -{ - GtkCellRendererClass *klass; - int height; - - g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); - g_return_if_fail (GTK_IS_WIDGET (widget)); - g_return_if_fail (NULL != minimum_size || NULL != natural_size); - - gtk_cell_renderer_get_fixed_size (GTK_CELL_RENDERER (cell), NULL, &height); - - if (height < 0) - { - klass = GTK_CELL_RENDERER_GET_CLASS (cell); - klass->get_preferred_height (cell, widget, minimum_size, natural_size); - } - else - { - if (minimum_size) - *minimum_size = height; - if (natural_size) - *natural_size = height; - } - -#if DEBUG_CELL_SIZE_REQUEST - g_message ("%s returning minimum height: %d and natural height: %d", - G_OBJECT_TYPE_NAME (cell), - minimum_size ? *minimum_size : 20000, - natural_size ? *natural_size : 20000); -#endif -} - - -/** - * gtk_cell_renderer_get_preferred_width_for_height: - * @cell: a `GtkCellRenderer` instance - * @widget: the `GtkWidget` this cell will be rendering to - * @height: the size which is available for allocation - * @minimum_width: (out) (optional): location for storing the minimum size - * @natural_width: (out) (optional): location for storing the preferred size - * - * Retrieves a cell renderers’s minimum and natural width if it were rendered to - * @widget with the specified @height. - */ -void -gtk_cell_renderer_get_preferred_width_for_height (GtkCellRenderer *cell, - GtkWidget *widget, - int height, - int *minimum_width, - int *natural_width) -{ - GtkCellRendererClass *klass; - int width; - - g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); - g_return_if_fail (GTK_IS_WIDGET (widget)); - g_return_if_fail (NULL != minimum_width || NULL != natural_width); - - gtk_cell_renderer_get_fixed_size (GTK_CELL_RENDERER (cell), &width, NULL); - - if (width < 0) - { - klass = GTK_CELL_RENDERER_GET_CLASS (cell); - klass->get_preferred_width_for_height (cell, widget, height, minimum_width, natural_width); - } - else - { - if (minimum_width) - *minimum_width = width; - if (natural_width) - *natural_width = width; - } - -#if DEBUG_CELL_SIZE_REQUEST - g_message ("%s width for height: %d is minimum %d and natural: %d", - G_OBJECT_TYPE_NAME (cell), height, - minimum_width ? *minimum_width : 20000, - natural_width ? *natural_width : 20000); -#endif -} - -/** - * gtk_cell_renderer_get_preferred_height_for_width: - * @cell: a `GtkCellRenderer` instance - * @widget: the `GtkWidget` this cell will be rendering to - * @width: the size which is available for allocation - * @minimum_height: (out) (optional): location for storing the minimum size - * @natural_height: (out) (optional): location for storing the preferred size - * - * Retrieves a cell renderers’s minimum and natural height if it were rendered to - * @widget with the specified @width. - */ -void -gtk_cell_renderer_get_preferred_height_for_width (GtkCellRenderer *cell, - GtkWidget *widget, - int width, - int *minimum_height, - int *natural_height) -{ - GtkCellRendererClass *klass; - int height; - - g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); - g_return_if_fail (GTK_IS_WIDGET (widget)); - g_return_if_fail (NULL != minimum_height || NULL != natural_height); - - gtk_cell_renderer_get_fixed_size (GTK_CELL_RENDERER (cell), NULL, &height); - - if (height < 0) - { - klass = GTK_CELL_RENDERER_GET_CLASS (cell); - klass->get_preferred_height_for_width (cell, widget, width, minimum_height, natural_height); - } - else - { - if (minimum_height) - *minimum_height = height; - if (natural_height) - *natural_height = height; - } - -#if DEBUG_CELL_SIZE_REQUEST - g_message ("%s height for width: %d is minimum %d and natural: %d", - G_OBJECT_TYPE_NAME (cell), width, - minimum_height ? *minimum_height : 20000, - natural_height ? *natural_height : 20000); -#endif -} - -/** - * gtk_cell_renderer_get_preferred_size: - * @cell: a `GtkCellRenderer` instance - * @widget: the `GtkWidget` this cell will be rendering to - * @minimum_size: (out) (optional): location for storing the minimum size - * @natural_size: (out) (optional): location for storing the natural size - * - * Retrieves the minimum and natural size of a cell taking - * into account the widget’s preference for height-for-width management. - */ -void -gtk_cell_renderer_get_preferred_size (GtkCellRenderer *cell, - GtkWidget *widget, - GtkRequisition *minimum_size, - GtkRequisition *natural_size) -{ - int min_width, nat_width; - int min_height, nat_height; - - g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); - - if (gtk_cell_renderer_get_request_mode (cell) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH) - { - gtk_cell_renderer_get_preferred_width (cell, widget, &min_width, &nat_width); - - if (minimum_size) - { - minimum_size->width = min_width; - gtk_cell_renderer_get_preferred_height_for_width (cell, widget, min_width, - &minimum_size->height, NULL); - } - - if (natural_size) - { - natural_size->width = nat_width; - gtk_cell_renderer_get_preferred_height_for_width (cell, widget, nat_width, - NULL, &natural_size->height); - } - } - else /* GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT */ - { - gtk_cell_renderer_get_preferred_height (cell, widget, &min_height, &nat_height); - - if (minimum_size) - { - minimum_size->height = min_height; - gtk_cell_renderer_get_preferred_width_for_height (cell, widget, min_height, - &minimum_size->width, NULL); - } - - if (natural_size) - { - natural_size->height = nat_height; - gtk_cell_renderer_get_preferred_width_for_height (cell, widget, nat_height, - NULL, &natural_size->width); - } - } -} - -/** - * gtk_cell_renderer_get_aligned_area: - * @cell: a `GtkCellRenderer` instance - * @widget: the `GtkWidget` this cell will be rendering to - * @flags: render flags - * @cell_area: cell area which would be passed to gtk_cell_renderer_render() - * @aligned_area: (out): the return location for the space inside @cell_area - * that would actually be used to render. - * - * Gets the aligned area used by @cell inside @cell_area. Used for finding - * the appropriate edit and focus rectangle. - */ -void -gtk_cell_renderer_get_aligned_area (GtkCellRenderer *cell, - GtkWidget *widget, - GtkCellRendererState flags, - const GdkRectangle *cell_area, - GdkRectangle *aligned_area) -{ - GtkCellRendererClass *klass; - - g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); - g_return_if_fail (GTK_IS_WIDGET (widget)); - g_return_if_fail (cell_area != NULL); - g_return_if_fail (aligned_area != NULL); - - klass = GTK_CELL_RENDERER_GET_CLASS (cell); - klass->get_aligned_area (cell, widget, flags, cell_area, aligned_area); - - g_assert (aligned_area->x >= cell_area->x && aligned_area->x <= cell_area->x + cell_area->width); - g_assert (aligned_area->y >= cell_area->y && aligned_area->y <= cell_area->y + cell_area->height); - g_assert ((aligned_area->x - cell_area->x) + aligned_area->width <= cell_area->width); - g_assert ((aligned_area->y - cell_area->y) + aligned_area->height <= cell_area->height); -} - -/** - * gtk_cell_renderer_get_state: - * @cell: (nullable): a `GtkCellRenderer` - * @widget: (nullable): a `GtkWidget` - * @cell_state: cell renderer state - * - * Translates the cell renderer state to `GtkStateFlags`, - * based on the cell renderer and widget sensitivity, and - * the given `GtkCellRenderer`State. - * - * Returns: the widget state flags applying to @cell - **/ -GtkStateFlags -gtk_cell_renderer_get_state (GtkCellRenderer *cell, - GtkWidget *widget, - GtkCellRendererState cell_state) -{ - GtkStateFlags state = 0; - - g_return_val_if_fail (!cell || GTK_IS_CELL_RENDERER (cell), 0); - g_return_val_if_fail (!widget || GTK_IS_WIDGET (widget), 0); - - if (widget) - state |= gtk_widget_get_state_flags (widget); - - state &= ~(GTK_STATE_FLAG_FOCUSED | GTK_STATE_FLAG_PRELIGHT | GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_DROP_ACTIVE); - - if ((state & GTK_STATE_FLAG_INSENSITIVE) != 0 || - (cell && !gtk_cell_renderer_get_sensitive (cell)) || - (cell_state & GTK_CELL_RENDERER_INSENSITIVE) != 0) - { - state |= GTK_STATE_FLAG_INSENSITIVE; - } - else - { - if ((widget && gtk_widget_has_focus (widget)) && - (cell_state & GTK_CELL_RENDERER_FOCUSED) != 0) - state |= GTK_STATE_FLAG_FOCUSED; - - if ((cell_state & GTK_CELL_RENDERER_PRELIT) != 0) - state |= GTK_STATE_FLAG_PRELIGHT; - } - - if ((cell_state & GTK_CELL_RENDERER_SELECTED) != 0) - state |= GTK_STATE_FLAG_SELECTED; - - return state; -} - -/** - * gtk_cell_renderer_set_is_expander: - * @cell: a `GtkCellRenderer` - * @is_expander: whether @cell is an expander - * - * Sets whether the given `GtkCellRenderer` is an expander. - */ -void -gtk_cell_renderer_set_is_expander (GtkCellRenderer *cell, - gboolean is_expander) -{ - GtkCellRendererPrivate *priv = gtk_cell_renderer_get_instance_private (cell); - - g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); - - is_expander = !!is_expander; - - if (is_expander != priv->is_expander) - { - priv->is_expander = is_expander; - - g_object_notify (G_OBJECT (cell), "is-expander"); - } -} - -/** - * gtk_cell_renderer_get_is_expander: - * @cell: a `GtkCellRenderer` - * - * Checks whether the given `GtkCellRenderer` is an expander. - * - * Returns: %TRUE if @cell is an expander, and %FALSE otherwise - */ -gboolean -gtk_cell_renderer_get_is_expander (GtkCellRenderer *cell) -{ - GtkCellRendererPrivate *priv = gtk_cell_renderer_get_instance_private (cell); - - g_return_val_if_fail (GTK_IS_CELL_RENDERER (cell), FALSE); - - return priv->is_expander; -} - -/** - * gtk_cell_renderer_set_is_expanded: - * @cell: a `GtkCellRenderer` - * @is_expanded: whether @cell should be expanded - * - * Sets whether the given `GtkCellRenderer` is expanded. - */ -void -gtk_cell_renderer_set_is_expanded (GtkCellRenderer *cell, - gboolean is_expanded) -{ - GtkCellRendererPrivate *priv = gtk_cell_renderer_get_instance_private (cell); - - g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); - - is_expanded = !!is_expanded; - - if (is_expanded != priv->is_expanded) - { - priv->is_expanded = is_expanded; - - g_object_notify (G_OBJECT (cell), "is-expanded"); - } -} - -/** - * gtk_cell_renderer_get_is_expanded: - * @cell: a `GtkCellRenderer` - * - * Checks whether the given `GtkCellRenderer` is expanded. - * - * Returns: %TRUE if the cell renderer is expanded - */ -gboolean -gtk_cell_renderer_get_is_expanded (GtkCellRenderer *cell) -{ - GtkCellRendererPrivate *priv = gtk_cell_renderer_get_instance_private (cell); - - g_return_val_if_fail (GTK_IS_CELL_RENDERER (cell), FALSE); - - return priv->is_expanded; -} diff --git a/gtk/gtkcellrenderer.h b/gtk/gtkcellrenderer.h deleted file mode 100644 index 1d6034775a..0000000000 --- a/gtk/gtkcellrenderer.h +++ /dev/null @@ -1,313 +0,0 @@ -/* gtkcellrenderer.h - * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#ifndef __GTK_CELL_RENDERER_H__ -#define __GTK_CELL_RENDERER_H__ - -#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) -#error "Only can be included directly." -#endif - -#include - -G_BEGIN_DECLS - - -/** - * GtkCellRendererState: - * @GTK_CELL_RENDERER_SELECTED: The cell is currently selected, and - * probably has a selection colored background to render to. - * @GTK_CELL_RENDERER_PRELIT: The mouse is hovering over the cell. - * @GTK_CELL_RENDERER_INSENSITIVE: The cell is drawn in an insensitive manner - * @GTK_CELL_RENDERER_SORTED: The cell is in a sorted row - * @GTK_CELL_RENDERER_FOCUSED: The cell is in the focus row. - * @GTK_CELL_RENDERER_EXPANDABLE: The cell is in a row that can be expanded - * @GTK_CELL_RENDERER_EXPANDED: The cell is in a row that is expanded - * - * Tells how a cell is to be rendered. - */ -typedef enum -{ - GTK_CELL_RENDERER_SELECTED = 1 << 0, - GTK_CELL_RENDERER_PRELIT = 1 << 1, - GTK_CELL_RENDERER_INSENSITIVE = 1 << 2, - /* this flag means the cell is in the sort column/row */ - GTK_CELL_RENDERER_SORTED = 1 << 3, - GTK_CELL_RENDERER_FOCUSED = 1 << 4, - GTK_CELL_RENDERER_EXPANDABLE = 1 << 5, - GTK_CELL_RENDERER_EXPANDED = 1 << 6 -} GtkCellRendererState; - -/** - * GtkCellRendererMode: - * @GTK_CELL_RENDERER_MODE_INERT: The cell is just for display - * and cannot be interacted with. Note that this doesn’t mean that eg. the - * row being drawn can’t be selected -- just that a particular element of - * it cannot be individually modified. - * @GTK_CELL_RENDERER_MODE_ACTIVATABLE: The cell can be clicked. - * @GTK_CELL_RENDERER_MODE_EDITABLE: The cell can be edited or otherwise modified. - * - * Identifies how the user can interact with a particular cell. - */ -typedef enum -{ - GTK_CELL_RENDERER_MODE_INERT, - GTK_CELL_RENDERER_MODE_ACTIVATABLE, - GTK_CELL_RENDERER_MODE_EDITABLE -} GtkCellRendererMode; - -#define GTK_TYPE_CELL_RENDERER (gtk_cell_renderer_get_type ()) -#define GTK_CELL_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_RENDERER, GtkCellRenderer)) -#define GTK_CELL_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_CELL_RENDERER, GtkCellRendererClass)) -#define GTK_IS_CELL_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_RENDERER)) -#define GTK_IS_CELL_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_CELL_RENDERER)) -#define GTK_CELL_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CELL_RENDERER, GtkCellRendererClass)) - -typedef struct _GtkCellRenderer GtkCellRenderer; -typedef struct _GtkCellRendererPrivate GtkCellRendererPrivate; -typedef struct _GtkCellRendererClass GtkCellRendererClass; -typedef struct _GtkCellRendererClassPrivate GtkCellRendererClassPrivate; - -struct _GtkCellRenderer -{ - GInitiallyUnowned parent_instance; - - /*< private >*/ - GtkCellRendererPrivate *priv; -}; - -/** - * GtkCellRendererClass: - * @get_request_mode: Called to gets whether the cell renderer prefers - * a height-for-width layout or a width-for-height layout. - * @get_preferred_width: Called to get a renderer’s natural width. - * @get_preferred_height_for_width: Called to get a renderer’s natural height for width. - * @get_preferred_height: Called to get a renderer’s natural height. - * @get_preferred_width_for_height: Called to get a renderer’s natural width for height. - * @get_aligned_area: Called to get the aligned area used by @cell inside @cell_area. - * @snapshot: Called to snapshot the content of the `GtkCellRenderer`. - * @activate: Called to activate the content of the `GtkCellRenderer`. - * @start_editing: Called to initiate editing the content of the `GtkCellRenderer`. - * @editing_canceled: Signal gets emitted when the user cancels the process of editing a cell. - * @editing_started: Signal gets emitted when a cell starts to be edited. - */ -struct _GtkCellRendererClass -{ - /*< private >*/ - GInitiallyUnownedClass parent_class; - - /*< public >*/ - - /* vtable - not signals */ - GtkSizeRequestMode (* get_request_mode) (GtkCellRenderer *cell); - void (* get_preferred_width) (GtkCellRenderer *cell, - GtkWidget *widget, - int *minimum_size, - int *natural_size); - void (* get_preferred_height_for_width) (GtkCellRenderer *cell, - GtkWidget *widget, - int width, - int *minimum_height, - int *natural_height); - void (* get_preferred_height) (GtkCellRenderer *cell, - GtkWidget *widget, - int *minimum_size, - int *natural_size); - void (* get_preferred_width_for_height) (GtkCellRenderer *cell, - GtkWidget *widget, - int height, - int *minimum_width, - int *natural_width); - void (* get_aligned_area) (GtkCellRenderer *cell, - GtkWidget *widget, - GtkCellRendererState flags, - const GdkRectangle *cell_area, - GdkRectangle *aligned_area); - void (* snapshot) (GtkCellRenderer *cell, - GtkSnapshot *snapshot, - GtkWidget *widget, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - GtkCellRendererState flags); - gboolean (* activate) (GtkCellRenderer *cell, - GdkEvent *event, - GtkWidget *widget, - const char *path, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - GtkCellRendererState flags); - GtkCellEditable * (* start_editing) (GtkCellRenderer *cell, - GdkEvent *event, - GtkWidget *widget, - const char *path, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - GtkCellRendererState flags); - - /* Signals */ - void (* editing_canceled) (GtkCellRenderer *cell); - void (* editing_started) (GtkCellRenderer *cell, - GtkCellEditable *editable, - const char *path); - - /*< private >*/ - gpointer padding[8]; -}; - -GDK_AVAILABLE_IN_ALL -GType gtk_cell_renderer_get_type (void) G_GNUC_CONST; - -GDK_AVAILABLE_IN_ALL -GtkSizeRequestMode gtk_cell_renderer_get_request_mode (GtkCellRenderer *cell); -GDK_AVAILABLE_IN_ALL -void gtk_cell_renderer_get_preferred_width (GtkCellRenderer *cell, - GtkWidget *widget, - int *minimum_size, - int *natural_size); -GDK_AVAILABLE_IN_ALL -void gtk_cell_renderer_get_preferred_height_for_width (GtkCellRenderer *cell, - GtkWidget *widget, - int width, - int *minimum_height, - int *natural_height); -GDK_AVAILABLE_IN_ALL -void gtk_cell_renderer_get_preferred_height (GtkCellRenderer *cell, - GtkWidget *widget, - int *minimum_size, - int *natural_size); -GDK_AVAILABLE_IN_ALL -void gtk_cell_renderer_get_preferred_width_for_height (GtkCellRenderer *cell, - GtkWidget *widget, - int height, - int *minimum_width, - int *natural_width); -GDK_AVAILABLE_IN_ALL -void gtk_cell_renderer_get_preferred_size (GtkCellRenderer *cell, - GtkWidget *widget, - GtkRequisition *minimum_size, - GtkRequisition *natural_size); -GDK_AVAILABLE_IN_ALL -void gtk_cell_renderer_get_aligned_area (GtkCellRenderer *cell, - GtkWidget *widget, - GtkCellRendererState flags, - const GdkRectangle *cell_area, - GdkRectangle *aligned_area); -GDK_AVAILABLE_IN_ALL -void gtk_cell_renderer_snapshot (GtkCellRenderer *cell, - GtkSnapshot *snapshot, - GtkWidget *widget, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - GtkCellRendererState flags); -GDK_AVAILABLE_IN_ALL -gboolean gtk_cell_renderer_activate (GtkCellRenderer *cell, - GdkEvent *event, - GtkWidget *widget, - const char *path, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - GtkCellRendererState flags); -GDK_AVAILABLE_IN_ALL -GtkCellEditable *gtk_cell_renderer_start_editing (GtkCellRenderer *cell, - GdkEvent *event, - GtkWidget *widget, - const char *path, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - GtkCellRendererState flags); - -GDK_AVAILABLE_IN_ALL -void gtk_cell_renderer_set_fixed_size (GtkCellRenderer *cell, - int width, - int height); -GDK_AVAILABLE_IN_ALL -void gtk_cell_renderer_get_fixed_size (GtkCellRenderer *cell, - int *width, - int *height); - -GDK_AVAILABLE_IN_ALL -void gtk_cell_renderer_set_alignment (GtkCellRenderer *cell, - float xalign, - float yalign); -GDK_AVAILABLE_IN_ALL -void gtk_cell_renderer_get_alignment (GtkCellRenderer *cell, - float *xalign, - float *yalign); - -GDK_AVAILABLE_IN_ALL -void gtk_cell_renderer_set_padding (GtkCellRenderer *cell, - int xpad, - int ypad); -GDK_AVAILABLE_IN_ALL -void gtk_cell_renderer_get_padding (GtkCellRenderer *cell, - int *xpad, - int *ypad); - -GDK_AVAILABLE_IN_ALL -void gtk_cell_renderer_set_visible (GtkCellRenderer *cell, - gboolean visible); -GDK_AVAILABLE_IN_ALL -gboolean gtk_cell_renderer_get_visible (GtkCellRenderer *cell); - -GDK_AVAILABLE_IN_ALL -void gtk_cell_renderer_set_sensitive (GtkCellRenderer *cell, - gboolean sensitive); -GDK_AVAILABLE_IN_ALL -gboolean gtk_cell_renderer_get_sensitive (GtkCellRenderer *cell); - -GDK_AVAILABLE_IN_ALL -gboolean gtk_cell_renderer_is_activatable (GtkCellRenderer *cell); - -GDK_AVAILABLE_IN_ALL -void gtk_cell_renderer_set_is_expander (GtkCellRenderer *cell, - gboolean is_expander); -GDK_AVAILABLE_IN_ALL -gboolean gtk_cell_renderer_get_is_expander (GtkCellRenderer *cell); - -GDK_AVAILABLE_IN_ALL -void gtk_cell_renderer_set_is_expanded (GtkCellRenderer *cell, - gboolean is_expanded); -GDK_AVAILABLE_IN_ALL -gboolean gtk_cell_renderer_get_is_expanded (GtkCellRenderer *cell); - - - - -/* For use by cell renderer implementations only */ -GDK_AVAILABLE_IN_ALL -void gtk_cell_renderer_stop_editing (GtkCellRenderer *cell, - gboolean canceled); - - -void _gtk_cell_renderer_calc_offset (GtkCellRenderer *cell, - const GdkRectangle *cell_area, - GtkTextDirection direction, - int width, - int height, - int *x_offset, - int *y_offset); - -GDK_AVAILABLE_IN_ALL -GtkStateFlags gtk_cell_renderer_get_state (GtkCellRenderer *cell, - GtkWidget *widget, - GtkCellRendererState cell_state); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkCellRenderer, g_object_unref) - -G_END_DECLS - -#endif /* __GTK_CELL_RENDERER_H__ */ diff --git a/gtk/gtkcellrendereraccel.c b/gtk/gtkcellrendereraccel.c deleted file mode 100644 index f6286337a7..0000000000 --- a/gtk/gtkcellrendereraccel.c +++ /dev/null @@ -1,729 +0,0 @@ -/* gtkcellrendereraccel.h - * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#include "config.h" - -#include "gtkcellrendereraccel.h" - -#include -#include "gtkaccelgroup.h" -#include "gtkmarshalers.h" -#include "gtklabel.h" -#include "gtkmain.h" -#include "gtksizerequest.h" -#include "gtktypebuiltins.h" -#include "gtkprivate.h" -#include "gtkeventcontrollerkey.h" -#include "gtknative.h" -#include "gtkbinlayout.h" - - -/** - * GtkCellRendererAccel: - * - * Renders a keyboard accelerator in a cell - * - * `GtkCellRendererAccel` displays a keyboard accelerator (i.e. a key - * combination like `Control + a`). If the cell renderer is editable, - * the accelerator can be changed by simply typing the new combination. - */ - - -static void gtk_cell_renderer_accel_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec); -static void gtk_cell_renderer_accel_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec); -static void gtk_cell_renderer_accel_get_preferred_width - (GtkCellRenderer *cell, - GtkWidget *widget, - int *minimum_size, - int *natural_size); -static GtkCellEditable * - gtk_cell_renderer_accel_start_editing (GtkCellRenderer *cell, - GdkEvent *event, - GtkWidget *widget, - const char *path, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - GtkCellRendererState flags); -static char *convert_keysym_state_to_string (GtkCellRendererAccel *accel, - guint keysym, - GdkModifierType mask, - guint keycode); -static GtkWidget *gtk_cell_editable_widget_new (GtkCellRenderer *cell, - GtkCellRendererAccelMode mode, - const char *path); - -enum { - ACCEL_EDITED, - ACCEL_CLEARED, - LAST_SIGNAL -}; - -enum { - PROP_ACCEL_KEY = 1, - PROP_ACCEL_MODS, - PROP_KEYCODE, - PROP_ACCEL_MODE -}; - -static guint signals[LAST_SIGNAL] = { 0 }; - -typedef struct _GtkCellRendererAccelPrivate GtkCellRendererAccelPrivate; -typedef struct _GtkCellRendererAccelClass GtkCellRendererAccelClass; - -struct _GtkCellRendererAccel -{ - GtkCellRendererText parent; -}; - -struct _GtkCellRendererAccelClass -{ - GtkCellRendererTextClass parent_class; - - void (* accel_edited) (GtkCellRendererAccel *accel, - const char *path_string, - guint accel_key, - GdkModifierType accel_mods, - guint hardware_keycode); - - void (* accel_cleared) (GtkCellRendererAccel *accel, - const char *path_string); - - /* Padding for future expansion */ - void (*_gtk_reserved0) (void); - void (*_gtk_reserved1) (void); - void (*_gtk_reserved2) (void); - void (*_gtk_reserved3) (void); - void (*_gtk_reserved4) (void); -}; - -struct _GtkCellRendererAccelPrivate -{ - GtkWidget *sizing_label; - - GtkCellRendererAccelMode accel_mode; - GdkModifierType accel_mods; - guint accel_key; - guint keycode; -}; - -G_DEFINE_TYPE_WITH_PRIVATE (GtkCellRendererAccel, gtk_cell_renderer_accel, GTK_TYPE_CELL_RENDERER_TEXT) - -static void -gtk_cell_renderer_accel_init (GtkCellRendererAccel *cell_accel) -{ - char *text; - - text = convert_keysym_state_to_string (cell_accel, 0, 0, 0); - g_object_set (cell_accel, "text", text, NULL); - g_free (text); -} - -static void -gtk_cell_renderer_accel_dispose (GObject *object) -{ - GtkCellRendererAccelPrivate *priv = gtk_cell_renderer_accel_get_instance_private (GTK_CELL_RENDERER_ACCEL (object)); - - g_clear_object (&priv->sizing_label); - - G_OBJECT_CLASS (gtk_cell_renderer_accel_parent_class)->dispose (object); -} - -static void -gtk_cell_renderer_accel_class_init (GtkCellRendererAccelClass *cell_accel_class) -{ - GObjectClass *object_class; - GtkCellRendererClass *cell_renderer_class; - - object_class = G_OBJECT_CLASS (cell_accel_class); - cell_renderer_class = GTK_CELL_RENDERER_CLASS (cell_accel_class); - - object_class->set_property = gtk_cell_renderer_accel_set_property; - object_class->get_property = gtk_cell_renderer_accel_get_property; - object_class->dispose = gtk_cell_renderer_accel_dispose; - - cell_renderer_class->get_preferred_width = gtk_cell_renderer_accel_get_preferred_width; - cell_renderer_class->start_editing = gtk_cell_renderer_accel_start_editing; - - /** - * GtkCellRendererAccel:accel-key: - * - * The keyval of the accelerator. - */ - g_object_class_install_property (object_class, - PROP_ACCEL_KEY, - g_param_spec_uint ("accel-key", NULL, NULL, - 0, - G_MAXINT, - 0, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - /** - * GtkCellRendererAccel:accel-mods: - * - * The modifier mask of the accelerator. - */ - g_object_class_install_property (object_class, - PROP_ACCEL_MODS, - g_param_spec_flags ("accel-mods", NULL, NULL, - GDK_TYPE_MODIFIER_TYPE, - 0, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - /** - * GtkCellRendererAccel:keycode: - * - * The hardware keycode of the accelerator. Note that the hardware keycode is - * only relevant if the key does not have a keyval. Normally, the keyboard - * configuration should assign keyvals to all keys. - */ - g_object_class_install_property (object_class, - PROP_KEYCODE, - g_param_spec_uint ("keycode", NULL, NULL, - 0, - G_MAXINT, - 0, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - /** - * GtkCellRendererAccel:accel-mode: - * - * Determines if the edited accelerators are GTK accelerators. If - * they are, consumed modifiers are suppressed, only accelerators - * accepted by GTK are allowed, and the accelerators are rendered - * in the same way as they are in menus. - */ - g_object_class_install_property (object_class, - PROP_ACCEL_MODE, - g_param_spec_enum ("accel-mode", NULL, NULL, - GTK_TYPE_CELL_RENDERER_ACCEL_MODE, - GTK_CELL_RENDERER_ACCEL_MODE_GTK, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - /** - * GtkCellRendererAccel::accel-edited: - * @accel: the object reveiving the signal - * @path_string: the path identifying the row of the edited cell - * @accel_key: the new accelerator keyval - * @accel_mods: the new acclerator modifier mask - * @hardware_keycode: the keycode of the new accelerator - * - * Gets emitted when the user has selected a new accelerator. - */ - signals[ACCEL_EDITED] = g_signal_new (I_("accel-edited"), - GTK_TYPE_CELL_RENDERER_ACCEL, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkCellRendererAccelClass, accel_edited), - NULL, NULL, - _gtk_marshal_VOID__STRING_UINT_FLAGS_UINT, - G_TYPE_NONE, 4, - G_TYPE_STRING, - G_TYPE_UINT, - GDK_TYPE_MODIFIER_TYPE, - G_TYPE_UINT); - - /** - * GtkCellRendererAccel::accel-cleared: - * @accel: the object reveiving the signal - * @path_string: the path identifying the row of the edited cell - * - * Gets emitted when the user has removed the accelerator. - */ - signals[ACCEL_CLEARED] = g_signal_new (I_("accel-cleared"), - GTK_TYPE_CELL_RENDERER_ACCEL, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkCellRendererAccelClass, accel_cleared), - NULL, NULL, - NULL, - G_TYPE_NONE, 1, - G_TYPE_STRING); -} - - -/** - * gtk_cell_renderer_accel_new: - * - * Creates a new `GtkCellRendererAccel`. - * - * Returns: the new cell renderer - */ -GtkCellRenderer * -gtk_cell_renderer_accel_new (void) -{ - return g_object_new (GTK_TYPE_CELL_RENDERER_ACCEL, NULL); -} - -static char * -convert_keysym_state_to_string (GtkCellRendererAccel *accel, - guint keysym, - GdkModifierType mask, - guint keycode) -{ - GtkCellRendererAccelPrivate *priv = gtk_cell_renderer_accel_get_instance_private (accel); - - if (keysym == 0 && keycode == 0) - /* This label is displayed in a treeview cell displaying - * a disabled accelerator key combination. - */ - return g_strdup (C_("Accelerator", "Disabled")); - else - { - if (priv->accel_mode == GTK_CELL_RENDERER_ACCEL_MODE_GTK) - { - if (!gtk_accelerator_valid (keysym, mask)) - /* This label is displayed in a treeview cell displaying - * an accelerator key combination that is not valid according - * to gtk_accelerator_valid(). - */ - return g_strdup (C_("Accelerator", "Invalid")); - - return gtk_accelerator_get_label (keysym, mask); - } - else - { - char *name; - - name = gtk_accelerator_get_label_with_keycode (NULL, keysym, keycode, mask); - if (name == NULL) - name = gtk_accelerator_name_with_keycode (NULL, keysym, keycode, mask); - - return name; - } - } -} - -static void -gtk_cell_renderer_accel_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec) -{ - GtkCellRendererAccelPrivate *priv = gtk_cell_renderer_accel_get_instance_private (GTK_CELL_RENDERER_ACCEL (object)); - - switch (param_id) - { - case PROP_ACCEL_KEY: - g_value_set_uint (value, priv->accel_key); - break; - - case PROP_ACCEL_MODS: - g_value_set_flags (value, priv->accel_mods); - break; - - case PROP_KEYCODE: - g_value_set_uint (value, priv->keycode); - break; - - case PROP_ACCEL_MODE: - g_value_set_enum (value, priv->accel_mode); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); - } -} - -static void -gtk_cell_renderer_accel_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec) -{ - GtkCellRendererAccel *accel = GTK_CELL_RENDERER_ACCEL (object); - GtkCellRendererAccelPrivate *priv = gtk_cell_renderer_accel_get_instance_private (GTK_CELL_RENDERER_ACCEL (object)); - gboolean changed = FALSE; - - switch (param_id) - { - case PROP_ACCEL_KEY: - { - guint accel_key = g_value_get_uint (value); - - if (priv->accel_key != accel_key) - { - priv->accel_key = accel_key; - changed = TRUE; - g_object_notify (object, "accel-key"); - } - } - break; - - case PROP_ACCEL_MODS: - { - guint accel_mods = g_value_get_flags (value); - - if (priv->accel_mods != accel_mods) - { - priv->accel_mods = accel_mods; - changed = TRUE; - g_object_notify (object, "accel-mods"); - } - } - break; - case PROP_KEYCODE: - { - guint keycode = g_value_get_uint (value); - - if (priv->keycode != keycode) - { - priv->keycode = keycode; - changed = TRUE; - g_object_notify (object, "keycode"); - } - } - break; - - case PROP_ACCEL_MODE: - if (priv->accel_mode != g_value_get_enum (value)) - { - priv->accel_mode = g_value_get_enum (value); - g_object_notify (object, "accel-mode"); - } - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); - } - - if (changed) - { - char *text; - - text = convert_keysym_state_to_string (accel, priv->accel_key, priv->accel_mods, priv->keycode); - g_object_set (accel, "text", text, NULL); - g_free (text); - } -} - -static void -gtk_cell_renderer_accel_get_preferred_width (GtkCellRenderer *cell, - GtkWidget *widget, - int *minimum_size, - int *natural_size) - -{ - GtkCellRendererAccelPrivate *priv = gtk_cell_renderer_accel_get_instance_private (GTK_CELL_RENDERER_ACCEL (cell)); - GtkRequisition min_req, nat_req; - - if (priv->sizing_label == NULL) - { - priv->sizing_label = gtk_label_new (_("New accelerator…")); - g_object_ref_sink (priv->sizing_label); - } - - gtk_widget_get_preferred_size (priv->sizing_label, &min_req, &nat_req); - - GTK_CELL_RENDERER_CLASS (gtk_cell_renderer_accel_parent_class)->get_preferred_width (cell, widget, - minimum_size, natural_size); - - /* FIXME: need to take the cell_area et al. into account */ - if (minimum_size) - *minimum_size = MAX (*minimum_size, min_req.width); - if (natural_size) - *natural_size = MAX (*natural_size, nat_req.width); -} - -static GtkCellEditable * -gtk_cell_renderer_accel_start_editing (GtkCellRenderer *cell, - GdkEvent *event, - GtkWidget *widget, - const char *path, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - GtkCellRendererState flags) -{ - GtkCellRendererText *celltext = GTK_CELL_RENDERER_TEXT (cell); - GtkCellRendererAccel *accel = GTK_CELL_RENDERER_ACCEL (cell); - GtkCellRendererAccelPrivate *priv = gtk_cell_renderer_accel_get_instance_private (accel); - GtkWidget *editable; - gboolean is_editable; - - /* If the cell isn't editable we return NULL. */ - g_object_get (celltext, "editable", &is_editable, NULL); - if (!is_editable) - return NULL; - - editable = gtk_cell_editable_widget_new (cell, priv->accel_mode, path); - - return GTK_CELL_EDITABLE (editable); -} - -/* --------------------------------- */ - -typedef struct _GtkCellEditableWidget GtkCellEditableWidget; -typedef GtkWidgetClass GtkCellEditableWidgetClass; - -struct _GtkCellEditableWidget -{ - GtkWidget parent; - - gboolean editing_canceled; - GtkCellRendererAccelMode accel_mode; - char *path; - GtkCellRenderer *cell; - GtkWidget *label; -}; - -enum { - PROP_EDITING_CANCELED = 1, - PROP_MODE, - PROP_PATH -}; - -GType gtk_cell_editable_widget_get_type (void); -static void gtk_cell_editable_widget_cell_editable_init (GtkCellEditableIface *iface); - -G_DEFINE_TYPE_WITH_CODE (GtkCellEditableWidget, gtk_cell_editable_widget, GTK_TYPE_WIDGET, - G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_EDITABLE, gtk_cell_editable_widget_cell_editable_init)) - -static void -gtk_cell_editable_widget_start_editing (GtkCellEditable *cell_editable, - GdkEvent *event) -{ - /* do nothing, because we are pointless */ -} - -static void -gtk_cell_editable_widget_cell_editable_init (GtkCellEditableIface *iface) -{ - iface->start_editing = gtk_cell_editable_widget_start_editing; -} - -static gboolean -key_controller_modifiers (GtkEventControllerKey *key, - GdkModifierType state, - GtkWidget *widget) -{ - /* Ignore modifiers */ - return TRUE; -} - -static gboolean -key_controller_key_pressed (GtkEventControllerKey *key, - guint keyval, - guint keycode, - GdkModifierType state, - GtkWidget *widget) -{ - GtkCellEditableWidget *box = (GtkCellEditableWidget*)widget; - gboolean edited = FALSE; - gboolean cleared = FALSE; - GdkModifierType accel_mods = 0; - guint accel_key; - GdkEvent *event; - - event = gtk_event_controller_get_current_event (GTK_EVENT_CONTROLLER (key)); - if (!gdk_key_event_get_match (event, &accel_key, &accel_mods)) - return FALSE; - - if (accel_mods == 0) - { - switch (keyval) - { - case GDK_KEY_BackSpace: - cleared = TRUE; - G_GNUC_FALLTHROUGH; - case GDK_KEY_Escape: - goto out; - default: - break; - } - } - - if (box->accel_mode == GTK_CELL_RENDERER_ACCEL_MODE_GTK && - !gtk_accelerator_valid (accel_key, accel_mods)) - { - gtk_widget_error_bell (widget); - return TRUE; - } - - edited = TRUE; - - out: - gtk_grab_remove (widget); - gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (widget)); - gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (widget)); - - if (edited) - g_signal_emit (box->cell, signals[ACCEL_EDITED], 0, box->path, - accel_key, accel_mods, keycode); - else if (cleared) - g_signal_emit (box->cell, signals[ACCEL_CLEARED], 0, box->path); - - return TRUE; -} - -static void -gtk_cell_editable_widget_unrealize (GtkWidget *widget) -{ - gtk_grab_remove (widget); - - GTK_WIDGET_CLASS (gtk_cell_editable_widget_parent_class)->unrealize (widget); -} - -static void -gtk_cell_editable_widget_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - GtkCellEditableWidget *box = (GtkCellEditableWidget*)object; - - switch (prop_id) - { - case PROP_EDITING_CANCELED: - box->editing_canceled = g_value_get_boolean (value); - break; - case PROP_MODE: - box->accel_mode = g_value_get_enum (value); - break; - case PROP_PATH: - box->path = g_value_dup_string (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gtk_cell_editable_widget_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GtkCellEditableWidget *box = (GtkCellEditableWidget*)object; - - switch (prop_id) - { - case PROP_EDITING_CANCELED: - g_value_set_boolean (value, box->editing_canceled); - break; - case PROP_MODE: - g_value_set_enum (value, box->accel_mode); - break; - case PROP_PATH: - g_value_set_string (value, box->path); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gtk_cell_editable_widget_dispose (GObject *object) -{ - GtkCellEditableWidget *box = (GtkCellEditableWidget*)object; - - g_clear_pointer (&box->label, gtk_widget_unparent); - - G_OBJECT_CLASS (gtk_cell_editable_widget_parent_class)->dispose (object); -} - -static void -gtk_cell_editable_widget_finalize (GObject *object) -{ - GtkCellEditableWidget *box = (GtkCellEditableWidget*)object; - - g_free (box->path); - - G_OBJECT_CLASS (gtk_cell_editable_widget_parent_class)->finalize (object); -} - -static void -gtk_cell_editable_widget_class_init (GtkCellEditableWidgetClass *class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (class); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); - - object_class->finalize = gtk_cell_editable_widget_finalize; - object_class->dispose = gtk_cell_editable_widget_dispose; - object_class->set_property = gtk_cell_editable_widget_set_property; - object_class->get_property = gtk_cell_editable_widget_get_property; - - widget_class->unrealize = gtk_cell_editable_widget_unrealize; - - g_object_class_override_property (object_class, - PROP_EDITING_CANCELED, - "editing-canceled"); - - g_object_class_install_property (object_class, PROP_MODE, - g_param_spec_enum ("accel-mode", NULL, NULL, - GTK_TYPE_CELL_RENDERER_ACCEL_MODE, - GTK_CELL_RENDERER_ACCEL_MODE_GTK, - GTK_PARAM_READWRITE)); - - g_object_class_install_property (object_class, PROP_PATH, - g_param_spec_string ("path", NULL, NULL, - NULL, GTK_PARAM_READWRITE)); - - gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); - gtk_widget_class_set_css_name (widget_class, I_("acceleditor")); -} - -static void -gtk_cell_editable_widget_init (GtkCellEditableWidget *box) -{ - GtkWidget *widget = GTK_WIDGET (box); - GtkEventController *controller; - - gtk_widget_set_focusable (widget, TRUE); - - controller = gtk_event_controller_key_new (); - g_signal_connect (controller, "key-pressed", - G_CALLBACK (key_controller_key_pressed), box); - g_signal_connect (controller, "modifiers", - G_CALLBACK (key_controller_modifiers), box); - gtk_widget_add_controller (widget, controller); -} - -static GtkWidget * -gtk_cell_editable_widget_new (GtkCellRenderer *cell, - GtkCellRendererAccelMode mode, - const char *path) -{ - GtkCellEditableWidget *box; - - box = g_object_new (gtk_cell_editable_widget_get_type (), - "accel-mode", mode, - "path", path, - NULL); - box->cell = cell; - - box->label = gtk_label_new (NULL); - gtk_widget_set_halign (box->label, GTK_ALIGN_START); - gtk_widget_set_valign (box->label, GTK_ALIGN_CENTER); - - gtk_widget_set_state_flags (box->label, GTK_STATE_FLAG_SELECTED, TRUE); - - /* This label is displayed in a treeview cell displaying an accelerator - * when the cell is clicked to change the acelerator. - */ - gtk_label_set_text (GTK_LABEL (box->label), _("New accelerator…")); - - gtk_widget_set_parent (box->label, GTK_WIDGET (box)); - - gtk_grab_add (GTK_WIDGET (box)); - - return GTK_WIDGET (box); -} diff --git a/gtk/gtkcellrendereraccel.h b/gtk/gtkcellrendereraccel.h deleted file mode 100644 index bacf559992..0000000000 --- a/gtk/gtkcellrendereraccel.h +++ /dev/null @@ -1,59 +0,0 @@ -/* gtkcellrendereraccel.h - * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#ifndef __GTK_CELL_RENDERER_ACCEL_H__ -#define __GTK_CELL_RENDERER_ACCEL_H__ - -#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) -#error "Only can be included directly." -#endif - -#include - -G_BEGIN_DECLS - -#define GTK_TYPE_CELL_RENDERER_ACCEL (gtk_cell_renderer_accel_get_type ()) -#define GTK_CELL_RENDERER_ACCEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_RENDERER_ACCEL, GtkCellRendererAccel)) -#define GTK_IS_CELL_RENDERER_ACCEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_RENDERER_ACCEL)) - -typedef struct _GtkCellRendererAccel GtkCellRendererAccel; - -/** - * GtkCellRendererAccelMode: - * @GTK_CELL_RENDERER_ACCEL_MODE_GTK: GTK accelerators mode - * @GTK_CELL_RENDERER_ACCEL_MODE_OTHER: Other accelerator mode - * - * The available modes for [property@Gtk.CellRendererAccel:accel-mode]. - */ -typedef enum -{ - GTK_CELL_RENDERER_ACCEL_MODE_GTK, - GTK_CELL_RENDERER_ACCEL_MODE_OTHER -} GtkCellRendererAccelMode; - - -GDK_AVAILABLE_IN_ALL -GType gtk_cell_renderer_accel_get_type (void) G_GNUC_CONST; -GDK_AVAILABLE_IN_ALL -GtkCellRenderer *gtk_cell_renderer_accel_new (void); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkCellRendererAccel, g_object_unref) - -G_END_DECLS - - -#endif /* __GTK_CELL_RENDERER_ACCEL_H__ */ diff --git a/gtk/gtkcellrenderercombo.c b/gtk/gtkcellrenderercombo.c deleted file mode 100644 index 399721065b..0000000000 --- a/gtk/gtkcellrenderercombo.c +++ /dev/null @@ -1,510 +0,0 @@ -/* GtkCellRendererCombo - * Copyright (C) 2004 Lorenzo Gil Sanchez - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see . - */ - -#include "config.h" -#include - -#include "gtkentry.h" -#include "gtkcelllayout.h" -#include "gtkcellrenderercombo.h" -#include "gtkcellrenderertext.h" -#include "gtkcombobox.h" -#include "gtkmarshalers.h" -#include "gtkprivate.h" - - -/** - * GtkCellRendererCombo: - * - * Renders a combobox in a cell - * - * `GtkCellRendererCombo` renders text in a cell like `GtkCellRendererText` from - * which it is derived. But while `GtkCellRendererText` offers a simple entry to - * edit the text, `GtkCellRendererCombo` offers a `GtkComboBox` - * widget to edit the text. The values to display in the combo box are taken from - * the tree model specified in the `GtkCellRendererCombo`:model property. - * - * The combo cell renderer takes care of adding a text cell renderer to the combo - * box and sets it to display the column specified by its - * `GtkCellRendererCombo`:text-column property. Further properties of the combo box - * can be set in a handler for the `GtkCellRenderer::editing-started` signal. - */ - -typedef struct _GtkCellRendererComboPrivate GtkCellRendererComboPrivate; -typedef struct _GtkCellRendererComboClass GtkCellRendererComboClass; - -struct _GtkCellRendererCombo -{ - GtkCellRendererText parent; -}; - -struct _GtkCellRendererComboClass -{ - GtkCellRendererTextClass parent; -}; - - -struct _GtkCellRendererComboPrivate -{ - GtkTreeModel *model; - - GtkWidget *combo; - - gboolean has_entry; - - int text_column; - - gulong focus_out_id; -}; - - -static void gtk_cell_renderer_combo_finalize (GObject *object); -static void gtk_cell_renderer_combo_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec); - -static void gtk_cell_renderer_combo_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec); - -static GtkCellEditable *gtk_cell_renderer_combo_start_editing (GtkCellRenderer *cell, - GdkEvent *event, - GtkWidget *widget, - const char *path, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - GtkCellRendererState flags); - -enum { - PROP_0, - PROP_MODEL, - PROP_TEXT_COLUMN, - PROP_HAS_ENTRY -}; - -enum { - CHANGED, - LAST_SIGNAL -}; - -static guint cell_renderer_combo_signals[LAST_SIGNAL] = { 0, }; - -#define GTK_CELL_RENDERER_COMBO_PATH "gtk-cell-renderer-combo-path" - -G_DEFINE_TYPE_WITH_PRIVATE (GtkCellRendererCombo, gtk_cell_renderer_combo, GTK_TYPE_CELL_RENDERER_TEXT) - -static void -gtk_cell_renderer_combo_class_init (GtkCellRendererComboClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass); - - object_class->finalize = gtk_cell_renderer_combo_finalize; - object_class->get_property = gtk_cell_renderer_combo_get_property; - object_class->set_property = gtk_cell_renderer_combo_set_property; - - cell_class->start_editing = gtk_cell_renderer_combo_start_editing; - - /** - * GtkCellRendererCombo:model: - * - * Holds a tree model containing the possible values for the combo box. - * Use the text_column property to specify the column holding the values. - */ - g_object_class_install_property (object_class, - PROP_MODEL, - g_param_spec_object ("model", NULL, NULL, - GTK_TYPE_TREE_MODEL, - GTK_PARAM_READWRITE)); - - /** - * GtkCellRendererCombo:text-column: - * - * Specifies the model column which holds the possible values for the - * combo box. - * - * Note that this refers to the model specified in the model property, - * not the model backing the tree view to which - * this cell renderer is attached. - * - * `GtkCellRendererCombo` automatically adds a text cell renderer for - * this column to its combo box. - */ - g_object_class_install_property (object_class, - PROP_TEXT_COLUMN, - g_param_spec_int ("text-column", NULL, NULL, - -1, - G_MAXINT, - -1, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - /** - * GtkCellRendererCombo:has-entry: - * - * If %TRUE, the cell renderer will include an entry and allow to enter - * values other than the ones in the popup list. - */ - g_object_class_install_property (object_class, - PROP_HAS_ENTRY, - g_param_spec_boolean ("has-entry", NULL, NULL, - TRUE, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - - /** - * GtkCellRendererCombo::changed: - * @combo: the object on which the signal is emitted - * @path_string: a string of the path identifying the edited cell - * (relative to the tree view model) - * @new_iter: the new iter selected in the combo box - * (relative to the combo box model) - * - * This signal is emitted each time after the user selected an item in - * the combo box, either by using the mouse or the arrow keys. Contrary - * to GtkComboBox, GtkCellRendererCombo::changed is not emitted for - * changes made to a selected item in the entry. The argument @new_iter - * corresponds to the newly selected item in the combo box and it is relative - * to the GtkTreeModel set via the model property on GtkCellRendererCombo. - * - * Note that as soon as you change the model displayed in the tree view, - * the tree view will immediately cease the editing operating. This - * means that you most probably want to refrain from changing the model - * until the combo cell renderer emits the edited or editing_canceled signal. - */ - cell_renderer_combo_signals[CHANGED] = - g_signal_new (I_("changed"), - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - _gtk_marshal_VOID__STRING_BOXED, - G_TYPE_NONE, 2, - G_TYPE_STRING, - GTK_TYPE_TREE_ITER); - g_signal_set_va_marshaller (cell_renderer_combo_signals[CHANGED], - G_TYPE_FROM_CLASS (object_class), - _gtk_marshal_VOID__STRING_BOXEDv); -} - -static void -gtk_cell_renderer_combo_init (GtkCellRendererCombo *self) -{ - GtkCellRendererComboPrivate *priv = gtk_cell_renderer_combo_get_instance_private (self); - - priv->model = NULL; - priv->text_column = -1; - priv->has_entry = TRUE; - priv->focus_out_id = 0; -} - -/** - * gtk_cell_renderer_combo_new: - * - * Creates a new `GtkCellRendererCombo`. - * Adjust how text is drawn using object properties. - * Object properties can be set globally (with g_object_set()). - * Also, with `GtkTreeViewColumn`, you can bind a property to a value - * in a `GtkTreeModel`. For example, you can bind the “text” property - * on the cell renderer to a string value in the model, thus rendering - * a different string in each row of the `GtkTreeView`. - * - * Returns: the new cell renderer - */ -GtkCellRenderer * -gtk_cell_renderer_combo_new (void) -{ - return g_object_new (GTK_TYPE_CELL_RENDERER_COMBO, NULL); -} - -static void -gtk_cell_renderer_combo_finalize (GObject *object) -{ - GtkCellRendererCombo *cell = GTK_CELL_RENDERER_COMBO (object); - GtkCellRendererComboPrivate *priv = gtk_cell_renderer_combo_get_instance_private (cell); - - if (priv->model) - { - g_object_unref (priv->model); - priv->model = NULL; - } - - G_OBJECT_CLASS (gtk_cell_renderer_combo_parent_class)->finalize (object); -} - -static void -gtk_cell_renderer_combo_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GtkCellRendererCombo *cell = GTK_CELL_RENDERER_COMBO (object); - GtkCellRendererComboPrivate *priv = gtk_cell_renderer_combo_get_instance_private (cell); - - switch (prop_id) - { - case PROP_MODEL: - g_value_set_object (value, priv->model); - break; - case PROP_TEXT_COLUMN: - g_value_set_int (value, priv->text_column); - break; - case PROP_HAS_ENTRY: - g_value_set_boolean (value, priv->has_entry); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gtk_cell_renderer_combo_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - GtkCellRendererCombo *cell = GTK_CELL_RENDERER_COMBO (object); - GtkCellRendererComboPrivate *priv = gtk_cell_renderer_combo_get_instance_private (cell); - - switch (prop_id) - { - case PROP_MODEL: - { - if (priv->model) - g_object_unref (priv->model); - priv->model = GTK_TREE_MODEL (g_value_get_object (value)); - if (priv->model) - g_object_ref (priv->model); - break; - } - case PROP_TEXT_COLUMN: - if (priv->text_column != g_value_get_int (value)) - { - priv->text_column = g_value_get_int (value); - g_object_notify_by_pspec (object, pspec); - } - break; - case PROP_HAS_ENTRY: - if (priv->has_entry != g_value_get_boolean (value)) - { - priv->has_entry = g_value_get_boolean (value); - g_object_notify_by_pspec (object, pspec); - } - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gtk_cell_renderer_combo_changed (GtkComboBox *combo, - gpointer data) -{ - GtkTreeIter iter; - GtkCellRendererCombo *cell; - - cell = GTK_CELL_RENDERER_COMBO (data); - - if (gtk_combo_box_get_active_iter (combo, &iter)) - { - const char *path; - - path = g_object_get_data (G_OBJECT (combo), GTK_CELL_RENDERER_COMBO_PATH); - g_signal_emit (cell, cell_renderer_combo_signals[CHANGED], 0, - path, &iter); - } -} - -static void -gtk_cell_renderer_combo_editing_done (GtkCellEditable *combo, - gpointer data) -{ - GtkCellRendererCombo *cell = GTK_CELL_RENDERER_COMBO (data); - GtkCellRendererComboPrivate *priv = gtk_cell_renderer_combo_get_instance_private (cell); - const char *path; - char *new_text = NULL; - GtkTreeModel *model; - GtkTreeIter iter; - GtkEntry *entry; - gboolean canceled; - - if (priv->focus_out_id > 0) - { - g_signal_handler_disconnect (combo, priv->focus_out_id); - priv->focus_out_id = 0; - } - - g_object_get (combo, - "editing-canceled", &canceled, - NULL); - gtk_cell_renderer_stop_editing (GTK_CELL_RENDERER (data), canceled); - if (canceled) - { - priv->combo = NULL; - return; - } - - if (gtk_combo_box_get_has_entry (GTK_COMBO_BOX (combo))) - { - entry = GTK_ENTRY (gtk_combo_box_get_child (GTK_COMBO_BOX (combo))); - new_text = g_strdup (gtk_editable_get_text (GTK_EDITABLE (entry))); - } - else - { - model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo)); - - if (model - && gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter)) - gtk_tree_model_get (model, &iter, priv->text_column, &new_text, -1); - } - - path = g_object_get_data (G_OBJECT (combo), GTK_CELL_RENDERER_COMBO_PATH); - g_signal_emit_by_name (cell, "edited", path, new_text); - - priv->combo = NULL; - - g_free (new_text); -} - -static void -gtk_cell_renderer_combo_focus_change (GtkWidget *widget, - GParamSpec *pspec, - gpointer data) -{ - if (!gtk_widget_has_focus (widget)) - gtk_cell_renderer_combo_editing_done (GTK_CELL_EDITABLE (widget), data); -} - -typedef struct -{ - GtkCellRendererCombo *cell; - gboolean found; - GtkTreeIter iter; -} SearchData; - -static gboolean -find_text (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data) -{ - SearchData *search_data = (SearchData *)data; - GtkCellRendererComboPrivate *priv = gtk_cell_renderer_combo_get_instance_private (search_data->cell); - char *text, *cell_text; - - gtk_tree_model_get (model, iter, priv->text_column, &text, -1); - g_object_get (GTK_CELL_RENDERER_TEXT (search_data->cell), - "text", &cell_text, - NULL); - if (text && cell_text && g_strcmp0 (text, cell_text) == 0) - { - search_data->iter = *iter; - search_data->found = TRUE; - } - - g_free (cell_text); - g_free (text); - - return search_data->found; -} - -static GtkCellEditable * -gtk_cell_renderer_combo_start_editing (GtkCellRenderer *cell, - GdkEvent *event, - GtkWidget *widget, - const char *path, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - GtkCellRendererState flags) -{ - GtkCellRendererCombo *cell_combo = GTK_CELL_RENDERER_COMBO (cell); - GtkCellRendererComboPrivate *priv = gtk_cell_renderer_combo_get_instance_private (cell_combo); - GtkWidget *combo; - SearchData search_data; - gboolean editable; - char *text; - - g_object_get (cell, "editable", &editable, NULL); - if (editable == FALSE) - return NULL; - - if (priv->text_column < 0) - return NULL; - - if (priv->has_entry) - { - combo = g_object_new (GTK_TYPE_COMBO_BOX, "has-entry", TRUE, NULL); - - if (priv->model) - gtk_combo_box_set_model (GTK_COMBO_BOX (combo), priv->model); - gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX (combo), - priv->text_column); - - g_object_get (cell, "text", &text, NULL); - if (text) - gtk_editable_set_text (GTK_EDITABLE (gtk_combo_box_get_child (GTK_COMBO_BOX (combo))), text); - g_free (text); - } - else - { - cell = gtk_cell_renderer_text_new (); - - combo = gtk_combo_box_new (); - if (priv->model) - gtk_combo_box_set_model (GTK_COMBO_BOX (combo), priv->model); - - gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, TRUE); - gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), - cell, "text", priv->text_column, - NULL); - - /* determine the current value */ - if (priv->model) - { - search_data.cell = cell_combo; - search_data.found = FALSE; - gtk_tree_model_foreach (priv->model, find_text, &search_data); - if (search_data.found) - gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo), - &(search_data.iter)); - } - } - - g_object_set (combo, "has-frame", FALSE, NULL); - g_object_set_data_full (G_OBJECT (combo), - I_(GTK_CELL_RENDERER_COMBO_PATH), - g_strdup (path), g_free); - - gtk_widget_show (combo); - - g_signal_connect (GTK_CELL_EDITABLE (combo), "editing-done", - G_CALLBACK (gtk_cell_renderer_combo_editing_done), - cell_combo); - g_signal_connect (GTK_CELL_EDITABLE (combo), "changed", - G_CALLBACK (gtk_cell_renderer_combo_changed), - cell_combo); - priv->focus_out_id = g_signal_connect (combo, "notify::has-focus", - G_CALLBACK (gtk_cell_renderer_combo_focus_change), - cell_combo); - - priv->combo = combo; - - return GTK_CELL_EDITABLE (combo); -} diff --git a/gtk/gtkcellrenderercombo.h b/gtk/gtkcellrenderercombo.h deleted file mode 100644 index 5f7eb6fba9..0000000000 --- a/gtk/gtkcellrenderercombo.h +++ /dev/null @@ -1,45 +0,0 @@ -/* GtkCellRendererCombo - * Copyright (C) 2004 Lorenzo Gil Sanchez - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see . - */ - -#ifndef __GTK_CELL_RENDERER_COMBO_H__ -#define __GTK_CELL_RENDERER_COMBO_H__ - -#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) -#error "Only can be included directly." -#endif - -#include -#include - -G_BEGIN_DECLS - -#define GTK_TYPE_CELL_RENDERER_COMBO (gtk_cell_renderer_combo_get_type ()) -#define GTK_CELL_RENDERER_COMBO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_RENDERER_COMBO, GtkCellRendererCombo)) -#define GTK_IS_CELL_RENDERER_COMBO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_RENDERER_COMBO)) - -typedef struct _GtkCellRendererCombo GtkCellRendererCombo; - -GDK_AVAILABLE_IN_ALL -GType gtk_cell_renderer_combo_get_type (void) G_GNUC_CONST; -GDK_AVAILABLE_IN_ALL -GtkCellRenderer *gtk_cell_renderer_combo_new (void); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkCellRendererCombo, g_object_unref) - -G_END_DECLS - -#endif /* __GTK_CELL_RENDERER_COMBO_H__ */ diff --git a/gtk/gtkcellrendererpixbuf.c b/gtk/gtkcellrendererpixbuf.c deleted file mode 100644 index b0c541123c..0000000000 --- a/gtk/gtkcellrendererpixbuf.c +++ /dev/null @@ -1,591 +0,0 @@ -/* gtkcellrendererpixbuf.c - * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#include "config.h" - -#include "gtkcellrendererpixbuf.h" - -#include "gtkiconhelperprivate.h" -#include "gtkicontheme.h" -#include "gtkprivate.h" -#include "gtksnapshot.h" -#include "gtkstylecontextprivate.h" -#include "gtktypebuiltins.h" - -#include -#include - -/** - * GtkCellRendererPixbuf: - * - * Renders a pixbuf in a cell - * - * A `GtkCellRendererPixbuf` can be used to render an image in a cell. It allows - * to render either a given `GdkPixbuf` (set via the - * `GtkCellRendererPixbuf:pixbuf` property) or a named icon (set via the - * `GtkCellRendererPixbuf:icon-name` property). - * - * To support the tree view, `GtkCellRendererPixbuf` also supports rendering two - * alternative pixbufs, when the `GtkCellRenderer:is-expander` property is %TRUE. - * If the `GtkCellRenderer:is-expanded property` is %TRUE and the - * `GtkCellRendererPixbuf:pixbuf-expander-open` property is set to a pixbuf, it - * renders that pixbuf, if the `GtkCellRenderer:is-expanded` property is %FALSE - * and the `GtkCellRendererPixbuf:pixbuf-expander-closed` property is set to a - * pixbuf, it renders that one. - */ - - -static void gtk_cell_renderer_pixbuf_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec); -static void gtk_cell_renderer_pixbuf_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec); -static void gtk_cell_renderer_pixbuf_get_size (GtkCellRendererPixbuf *self, - GtkWidget *widget, - const GdkRectangle *rectangle, - int *x_offset, - int *y_offset, - int *width, - int *height); -static void gtk_cell_renderer_pixbuf_snapshot (GtkCellRenderer *cell, - GtkSnapshot *snapshot, - GtkWidget *widget, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - GtkCellRendererState flags); - - -enum { - PROP_0, - PROP_PIXBUF, - PROP_PIXBUF_EXPANDER_OPEN, - PROP_PIXBUF_EXPANDER_CLOSED, - PROP_TEXTURE, - PROP_ICON_SIZE, - PROP_ICON_NAME, - PROP_GICON -}; - -typedef struct _GtkCellRendererPixbufPrivate GtkCellRendererPixbufPrivate; -typedef struct _GtkCellRendererPixbufClass GtkCellRendererPixbufClass; - -struct _GtkCellRendererPixbuf -{ - GtkCellRenderer parent; -}; - -struct _GtkCellRendererPixbufClass -{ - GtkCellRendererClass parent_class; -}; - -struct _GtkCellRendererPixbufPrivate -{ - GtkImageDefinition *image_def; - GtkIconSize icon_size; - - GdkPixbuf *pixbuf_expander_open; - GdkPixbuf *pixbuf_expander_closed; - GdkTexture *texture_expander_open; - GdkTexture *texture_expander_closed; -}; - -G_DEFINE_TYPE_WITH_PRIVATE (GtkCellRendererPixbuf, gtk_cell_renderer_pixbuf, GTK_TYPE_CELL_RENDERER) - -static void -gtk_cell_renderer_pixbuf_init (GtkCellRendererPixbuf *cellpixbuf) -{ - GtkCellRendererPixbufPrivate *priv = gtk_cell_renderer_pixbuf_get_instance_private (cellpixbuf); - - priv->image_def = gtk_image_definition_new_empty (); -} - -static void -gtk_cell_renderer_pixbuf_finalize (GObject *object) -{ - GtkCellRendererPixbuf *cellpixbuf = GTK_CELL_RENDERER_PIXBUF (object); - GtkCellRendererPixbufPrivate *priv = gtk_cell_renderer_pixbuf_get_instance_private (cellpixbuf); - - gtk_image_definition_unref (priv->image_def); - - g_clear_object (&priv->pixbuf_expander_open); - g_clear_object (&priv->pixbuf_expander_closed); - g_clear_object (&priv->texture_expander_open); - g_clear_object (&priv->texture_expander_closed); - - G_OBJECT_CLASS (gtk_cell_renderer_pixbuf_parent_class)->finalize (object); -} - -static GtkSizeRequestMode -gtk_cell_renderer_pixbuf_get_request_mode (GtkCellRenderer *cell) -{ - return GTK_SIZE_REQUEST_CONSTANT_SIZE; -} - -static void -gtk_cell_renderer_pixbuf_get_preferred_width (GtkCellRenderer *cell, - GtkWidget *widget, - int *minimum, - int *natural) -{ - int size = 0; - - gtk_cell_renderer_pixbuf_get_size (GTK_CELL_RENDERER_PIXBUF (cell), widget, NULL, - NULL, NULL, &size, NULL); - - if (minimum != NULL) - *minimum = size; - - if (natural != NULL) - *natural = size; -} - -static void -gtk_cell_renderer_pixbuf_get_preferred_height (GtkCellRenderer *cell, - GtkWidget *widget, - int *minimum, - int *natural) -{ - int size = 0; - - gtk_cell_renderer_pixbuf_get_size (GTK_CELL_RENDERER_PIXBUF (cell), widget, NULL, - NULL, NULL, NULL, &size); - - if (minimum != NULL) - *minimum = size; - - if (natural != NULL) - *natural = size; -} - -static void -gtk_cell_renderer_pixbuf_class_init (GtkCellRendererPixbufClass *class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (class); - GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (class); - - object_class->finalize = gtk_cell_renderer_pixbuf_finalize; - - object_class->get_property = gtk_cell_renderer_pixbuf_get_property; - object_class->set_property = gtk_cell_renderer_pixbuf_set_property; - - cell_class->get_request_mode = gtk_cell_renderer_pixbuf_get_request_mode; - cell_class->get_preferred_width = gtk_cell_renderer_pixbuf_get_preferred_width; - cell_class->get_preferred_height = gtk_cell_renderer_pixbuf_get_preferred_height; - cell_class->snapshot = gtk_cell_renderer_pixbuf_snapshot; - - g_object_class_install_property (object_class, - PROP_PIXBUF, - g_param_spec_object ("pixbuf", NULL, NULL, - GDK_TYPE_PIXBUF, - GTK_PARAM_WRITABLE)); - - g_object_class_install_property (object_class, - PROP_PIXBUF_EXPANDER_OPEN, - g_param_spec_object ("pixbuf-expander-open", NULL, NULL, - GDK_TYPE_PIXBUF, - GTK_PARAM_READWRITE)); - - g_object_class_install_property (object_class, - PROP_PIXBUF_EXPANDER_CLOSED, - g_param_spec_object ("pixbuf-expander-closed", NULL, NULL, - GDK_TYPE_PIXBUF, - GTK_PARAM_READWRITE)); - - /** - * GtkCellRendererPixbuf:texture: - */ - g_object_class_install_property (object_class, - PROP_TEXTURE, - g_param_spec_object ("texture", NULL, NULL, - GDK_TYPE_TEXTURE, - GTK_PARAM_READWRITE)); - - /** - * GtkCellRendererPixbuf:icon-size: - * - * The `GtkIconSize` value that specifies the size of the rendered icon. - */ - g_object_class_install_property (object_class, - PROP_ICON_SIZE, - g_param_spec_enum ("icon-size", NULL, NULL, - GTK_TYPE_ICON_SIZE, - GTK_ICON_SIZE_INHERIT, - GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY)); - - /** - * GtkCellRendererPixbuf:icon-name: - * - * The name of the themed icon to display. - * This property only has an effect if not overridden by the "pixbuf" property. - */ - g_object_class_install_property (object_class, - PROP_ICON_NAME, - g_param_spec_string ("icon-name", NULL, NULL, - NULL, - GTK_PARAM_READWRITE)); - - /** - * GtkCellRendererPixbuf:gicon: - * - * The GIcon representing the icon to display. - * If the icon theme is changed, the image will be updated - * automatically. - */ - g_object_class_install_property (object_class, - PROP_GICON, - g_param_spec_object ("gicon", NULL, NULL, - G_TYPE_ICON, - GTK_PARAM_READWRITE)); -} - -static void -gtk_cell_renderer_pixbuf_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec) -{ - GtkCellRendererPixbuf *cellpixbuf = GTK_CELL_RENDERER_PIXBUF (object); - GtkCellRendererPixbufPrivate *priv = gtk_cell_renderer_pixbuf_get_instance_private (cellpixbuf); - - switch (param_id) - { - case PROP_PIXBUF_EXPANDER_OPEN: - g_value_set_object (value, priv->pixbuf_expander_open); - break; - case PROP_PIXBUF_EXPANDER_CLOSED: - g_value_set_object (value, priv->pixbuf_expander_closed); - break; - case PROP_TEXTURE: - g_value_set_object (value, gtk_image_definition_get_paintable (priv->image_def)); - break; - case PROP_ICON_SIZE: - g_value_set_enum (value, priv->icon_size); - break; - case PROP_ICON_NAME: - g_value_set_string (value, gtk_image_definition_get_icon_name (priv->image_def)); - break; - case PROP_GICON: - g_value_set_object (value, gtk_image_definition_get_gicon (priv->image_def)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); - break; - } -} - -static void -notify_storage_type (GtkCellRendererPixbuf *cellpixbuf, - GtkImageType storage_type) -{ - switch (storage_type) - { - case GTK_IMAGE_PAINTABLE: - g_object_notify (G_OBJECT (cellpixbuf), "texture"); - break; - case GTK_IMAGE_ICON_NAME: - g_object_notify (G_OBJECT (cellpixbuf), "icon-name"); - break; - case GTK_IMAGE_GICON: - g_object_notify (G_OBJECT (cellpixbuf), "gicon"); - break; - default: - g_assert_not_reached (); - case GTK_IMAGE_EMPTY: - break; - } -} - -static void -take_image_definition (GtkCellRendererPixbuf *cellpixbuf, - GtkImageDefinition *def) -{ - GtkCellRendererPixbufPrivate *priv = gtk_cell_renderer_pixbuf_get_instance_private (cellpixbuf); - GtkImageType old_storage_type, new_storage_type; - - if (def == NULL) - def = gtk_image_definition_new_empty (); - - old_storage_type = gtk_image_definition_get_storage_type (priv->image_def); - new_storage_type = gtk_image_definition_get_storage_type (def); - - if (new_storage_type != old_storage_type) - notify_storage_type (cellpixbuf, old_storage_type); - - gtk_image_definition_unref (priv->image_def); - priv->image_def = def; -} - -static void -gtk_cell_renderer_pixbuf_set_icon_size (GtkCellRendererPixbuf *cellpixbuf, - GtkIconSize icon_size) -{ - GtkCellRendererPixbufPrivate *priv = gtk_cell_renderer_pixbuf_get_instance_private (cellpixbuf); - - if (priv->icon_size == icon_size) - return; - - priv->icon_size = icon_size; - g_object_notify (G_OBJECT (cellpixbuf), "icon-size"); -} - -static void -gtk_cell_renderer_pixbuf_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec) -{ - GtkCellRendererPixbuf *cellpixbuf = GTK_CELL_RENDERER_PIXBUF (object); - GtkCellRendererPixbufPrivate *priv = gtk_cell_renderer_pixbuf_get_instance_private (cellpixbuf); - GdkTexture *texture; - GdkPixbuf *pixbuf; - - switch (param_id) - { - case PROP_PIXBUF: - pixbuf = g_value_get_object (value); - if (pixbuf) - texture = gdk_texture_new_for_pixbuf (pixbuf); - else - texture = NULL; - take_image_definition (cellpixbuf, gtk_image_definition_new_paintable (GDK_PAINTABLE (texture))); - break; - case PROP_PIXBUF_EXPANDER_OPEN: - g_clear_object (&priv->pixbuf_expander_open); - g_clear_object (&priv->texture_expander_open); - priv->pixbuf_expander_open = (GdkPixbuf*) g_value_dup_object (value); - priv->texture_expander_open = gdk_texture_new_for_pixbuf (priv->pixbuf_expander_open); - break; - case PROP_PIXBUF_EXPANDER_CLOSED: - g_clear_object (&priv->pixbuf_expander_closed); - g_clear_object (&priv->texture_expander_closed); - priv->pixbuf_expander_closed = (GdkPixbuf*) g_value_dup_object (value); - priv->texture_expander_closed = gdk_texture_new_for_pixbuf (priv->pixbuf_expander_open); - break; - case PROP_TEXTURE: - take_image_definition (cellpixbuf, gtk_image_definition_new_paintable (g_value_get_object (value))); - break; - case PROP_ICON_SIZE: - gtk_cell_renderer_pixbuf_set_icon_size (cellpixbuf, g_value_get_enum (value)); - break; - case PROP_ICON_NAME: - take_image_definition (cellpixbuf, gtk_image_definition_new_icon_name (g_value_get_string (value))); - break; - case PROP_GICON: - take_image_definition (cellpixbuf, gtk_image_definition_new_gicon (g_value_get_object (value))); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); - break; - } -} - -/** - * gtk_cell_renderer_pixbuf_new: - * - * Creates a new `GtkCellRendererPixbuf`. Adjust rendering - * parameters using object properties. Object properties can be set - * globally (with g_object_set()). Also, with `GtkTreeViewColumn`, you - * can bind a property to a value in a `GtkTreeModel`. For example, you - * can bind the “pixbuf” property on the cell renderer to a pixbuf value - * in the model, thus rendering a different image in each row of the - * `GtkTreeView`. - * - * Returns: the new cell renderer - **/ -GtkCellRenderer * -gtk_cell_renderer_pixbuf_new (void) -{ - return g_object_new (GTK_TYPE_CELL_RENDERER_PIXBUF, NULL); -} - -static GtkIconHelper * -create_icon_helper (GtkCellRendererPixbuf *cellpixbuf, - GtkWidget *widget) -{ - GtkCellRendererPixbufPrivate *priv = gtk_cell_renderer_pixbuf_get_instance_private (cellpixbuf); - GtkIconHelper *icon_helper; - - icon_helper = gtk_icon_helper_new (gtk_style_context_get_node (gtk_widget_get_style_context (widget)), - widget); - _gtk_icon_helper_set_use_fallback (icon_helper, TRUE); - _gtk_icon_helper_set_definition (icon_helper, priv->image_def); - - return icon_helper; -} - -static void -gtk_cell_renderer_pixbuf_get_size (GtkCellRendererPixbuf *self, - GtkWidget *widget, - const GdkRectangle *cell_area, - int *x_offset, - int *y_offset, - int *width, - int *height) -{ - GtkCellRendererPixbufPrivate *priv = gtk_cell_renderer_pixbuf_get_instance_private (self); - GtkCellRenderer *cell = GTK_CELL_RENDERER (self); - int pixbuf_width; - int pixbuf_height; - int calc_width; - int calc_height; - int xpad, ypad; - GtkStyleContext *context; - GtkIconHelper *icon_helper; - - context = gtk_widget_get_style_context (widget); - gtk_style_context_save (context); - gtk_style_context_add_class (context, "image"); - gtk_icon_size_set_style_classes (gtk_style_context_get_node (context), priv->icon_size); - icon_helper = create_icon_helper (self, widget); - - if (_gtk_icon_helper_get_is_empty (icon_helper)) - pixbuf_width = pixbuf_height = 0; - else if (gtk_image_definition_get_paintable (priv->image_def)) - { - GdkPaintable *paintable = gtk_image_definition_get_paintable (priv->image_def); - pixbuf_width = gdk_paintable_get_intrinsic_width (paintable); - pixbuf_height = gdk_paintable_get_intrinsic_height (paintable); - } - else - pixbuf_width = pixbuf_height = gtk_icon_helper_get_size (icon_helper); - - g_object_unref (icon_helper); - gtk_style_context_restore (context); - - if (priv->pixbuf_expander_open) - { - pixbuf_width = MAX (pixbuf_width, gdk_pixbuf_get_width (priv->pixbuf_expander_open)); - pixbuf_height = MAX (pixbuf_height, gdk_pixbuf_get_height (priv->pixbuf_expander_open)); - } - if (priv->pixbuf_expander_closed) - { - pixbuf_width = MAX (pixbuf_width, gdk_pixbuf_get_width (priv->pixbuf_expander_closed)); - pixbuf_height = MAX (pixbuf_height, gdk_pixbuf_get_height (priv->pixbuf_expander_closed)); - } - - gtk_cell_renderer_get_padding (cell, &xpad, &ypad); - calc_width = (int) xpad * 2 + pixbuf_width; - calc_height = (int) ypad * 2 + pixbuf_height; - - if (cell_area && pixbuf_width > 0 && pixbuf_height > 0) - { - float xalign, yalign; - - gtk_cell_renderer_get_alignment (cell, &xalign, &yalign); - if (x_offset) - { - *x_offset = (((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) ? - (1.0 - xalign) : xalign) * - (cell_area->width - calc_width)); - *x_offset = MAX (*x_offset, 0); - } - if (y_offset) - { - *y_offset = (yalign * - (cell_area->height - calc_height)); - *y_offset = MAX (*y_offset, 0); - } - } - else - { - if (x_offset) *x_offset = 0; - if (y_offset) *y_offset = 0; - } - - if (width) - *width = calc_width; - - if (height) - *height = calc_height; -} - -static void -gtk_cell_renderer_pixbuf_snapshot (GtkCellRenderer *cell, - GtkSnapshot *snapshot, - GtkWidget *widget, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - GtkCellRendererState flags) - -{ - GtkCellRendererPixbuf *cellpixbuf = GTK_CELL_RENDERER_PIXBUF (cell); - GtkCellRendererPixbufPrivate *priv = gtk_cell_renderer_pixbuf_get_instance_private (cellpixbuf); - GtkStyleContext *context; - GdkRectangle pix_rect; - gboolean is_expander; - int xpad, ypad; - GtkIconHelper *icon_helper; - - gtk_cell_renderer_pixbuf_get_size (cellpixbuf, widget, - cell_area, - &pix_rect.x, - &pix_rect.y, - &pix_rect.width, - &pix_rect.height); - - gtk_cell_renderer_get_padding (cell, &xpad, &ypad); - pix_rect.x += cell_area->x + xpad; - pix_rect.y += cell_area->y + ypad; - pix_rect.width -= xpad * 2; - pix_rect.height -= ypad * 2; - - if (!gdk_rectangle_intersect (cell_area, &pix_rect, NULL)) - return; - - context = gtk_widget_get_style_context (widget); - gtk_style_context_save (context); - - gtk_style_context_add_class (context, "image"); - gtk_icon_size_set_style_classes (gtk_style_context_get_node (context), priv->icon_size); - - is_expander = gtk_cell_renderer_get_is_expander (cell); - if (is_expander) - { - gboolean is_expanded = gtk_cell_renderer_get_is_expanded (cell);; - - if (is_expanded && priv->pixbuf_expander_open != NULL) - { - icon_helper = gtk_icon_helper_new (gtk_style_context_get_node (context), widget); - _gtk_icon_helper_set_paintable (icon_helper, GDK_PAINTABLE (priv->texture_expander_open)); - } - else if (!is_expanded && priv->pixbuf_expander_closed != NULL) - { - icon_helper = gtk_icon_helper_new (gtk_style_context_get_node (context), widget); - _gtk_icon_helper_set_paintable (icon_helper, GDK_PAINTABLE (priv->texture_expander_closed)); - } - else - { - icon_helper = create_icon_helper (cellpixbuf, widget); - } - } - else - { - icon_helper = create_icon_helper (cellpixbuf, widget); - } - - gtk_snapshot_save (snapshot); - gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (pix_rect.x, pix_rect.y)); - gdk_paintable_snapshot (GDK_PAINTABLE (icon_helper), snapshot, pix_rect.width, pix_rect.height); - gtk_snapshot_restore (snapshot); - - g_object_unref (icon_helper); - gtk_style_context_restore (context); -} diff --git a/gtk/gtkcellrendererpixbuf.h b/gtk/gtkcellrendererpixbuf.h deleted file mode 100644 index 6dd04d4d85..0000000000 --- a/gtk/gtkcellrendererpixbuf.h +++ /dev/null @@ -1,47 +0,0 @@ -/* gtkcellrendererpixbuf.h - * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#ifndef __GTK_CELL_RENDERER_PIXBUF_H__ -#define __GTK_CELL_RENDERER_PIXBUF_H__ - -#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) -#error "Only can be included directly." -#endif - -#include - - -G_BEGIN_DECLS - - -#define GTK_TYPE_CELL_RENDERER_PIXBUF (gtk_cell_renderer_pixbuf_get_type ()) -#define GTK_CELL_RENDERER_PIXBUF(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_RENDERER_PIXBUF, GtkCellRendererPixbuf)) -#define GTK_IS_CELL_RENDERER_PIXBUF(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_RENDERER_PIXBUF)) - -typedef struct _GtkCellRendererPixbuf GtkCellRendererPixbuf; - -GDK_AVAILABLE_IN_ALL -GType gtk_cell_renderer_pixbuf_get_type (void) G_GNUC_CONST; -GDK_AVAILABLE_IN_ALL -GtkCellRenderer *gtk_cell_renderer_pixbuf_new (void); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkCellRendererPixbuf, g_object_unref) - -G_END_DECLS - - -#endif /* __GTK_CELL_RENDERER_PIXBUF_H__ */ diff --git a/gtk/gtkcellrendererprogress.c b/gtk/gtkcellrendererprogress.c deleted file mode 100644 index 6bee958e69..0000000000 --- a/gtk/gtkcellrendererprogress.c +++ /dev/null @@ -1,726 +0,0 @@ -/* gtkcellrendererprogress.c - * Copyright (C) 2002 Naba Kumar - * heavily modified by Jörgen Scheibengruber - * heavily modified by Marco Pesenti Gritti - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ -/* - * Modified by the GTK+ Team and others 1997-2007. See the AUTHORS - * file for a list of people on the GTK+ Team. See the ChangeLog - * files for a list of changes. These files are distributed with - * GTK+ at ftp://ftp.gtk.org/pub/gtk/. - */ - -#include "config.h" -#include - -#include "gtkcellrendererprogress.h" -#include -#include "gtkorientable.h" -#include "gtkprivate.h" -#include "gtksnapshot.h" -#include "gtkstylecontext.h" - - -/** - * GtkCellRendererProgress: - * - * Renders numbers as progress bars - * - * `GtkCellRendererProgress` renders a numeric value as a progress par in a cell. - * Additionally, it can display a text on top of the progress bar. - */ - - -enum -{ - PROP_0, - PROP_VALUE, - PROP_TEXT, - PROP_PULSE, - PROP_TEXT_XALIGN, - PROP_TEXT_YALIGN, - PROP_ORIENTATION, - PROP_INVERTED -}; - -typedef struct _GtkCellRendererProgressClass GtkCellRendererProgressClass; -typedef struct _GtkCellRendererProgressPrivate GtkCellRendererProgressPrivate; - -struct _GtkCellRendererProgress -{ - GtkCellRenderer parent_instance; -}; - -struct _GtkCellRendererProgressClass -{ - GtkCellRendererClass parent_class; -}; - -struct _GtkCellRendererProgressPrivate -{ - int value; - char *text; - char *label; - int min_h; - int min_w; - int pulse; - int offset; - float text_xalign; - float text_yalign; - GtkOrientation orientation; - gboolean inverted; -}; - -static void gtk_cell_renderer_progress_finalize (GObject *object); -static void gtk_cell_renderer_progress_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec); -static void gtk_cell_renderer_progress_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec); -static void gtk_cell_renderer_progress_set_value (GtkCellRendererProgress *cellprogress, - int value); -static void gtk_cell_renderer_progress_set_text (GtkCellRendererProgress *cellprogress, - const char *text); -static void gtk_cell_renderer_progress_set_pulse (GtkCellRendererProgress *cellprogress, - int pulse); -static void compute_dimensions (GtkCellRenderer *cell, - GtkWidget *widget, - const char *text, - int *width, - int *height); -static void gtk_cell_renderer_progress_snapshot (GtkCellRenderer *cell, - GtkSnapshot *snapshot, - GtkWidget *widget, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - GtkCellRendererState flags); - - -G_DEFINE_TYPE_WITH_CODE (GtkCellRendererProgress, gtk_cell_renderer_progress, GTK_TYPE_CELL_RENDERER, - G_ADD_PRIVATE (GtkCellRendererProgress) - G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL)) - -static void -recompute_label (GtkCellRendererProgress *cellprogress) -{ - GtkCellRendererProgressPrivate *priv = gtk_cell_renderer_progress_get_instance_private (cellprogress); - char *label; - - if (priv->text) - label = g_strdup (priv->text); - else if (priv->pulse < 0) - label = g_strdup_printf (C_("progress bar label", "%d %%"), priv->value); - else - label = NULL; - - g_free (priv->label); - priv->label = label; -} - -static void -gtk_cell_renderer_progress_set_value (GtkCellRendererProgress *cellprogress, - int value) -{ - GtkCellRendererProgressPrivate *priv = gtk_cell_renderer_progress_get_instance_private (cellprogress); - - if (priv->value != value) - { - priv->value = value; - recompute_label (cellprogress); - g_object_notify (G_OBJECT (cellprogress), "value"); - } -} - -static void -gtk_cell_renderer_progress_set_text (GtkCellRendererProgress *cellprogress, - const char *text) -{ - GtkCellRendererProgressPrivate *priv = gtk_cell_renderer_progress_get_instance_private (cellprogress); - char *new_text; - - new_text = g_strdup (text); - g_free (priv->text); - priv->text = new_text; - recompute_label (cellprogress); - g_object_notify (G_OBJECT (cellprogress), "text"); -} - -static void -gtk_cell_renderer_progress_set_pulse (GtkCellRendererProgress *cellprogress, - int pulse) -{ - GtkCellRendererProgressPrivate *priv = gtk_cell_renderer_progress_get_instance_private (cellprogress); - - if (pulse != priv->pulse) - { - if (pulse <= 0) - priv->offset = 0; - else - priv->offset = pulse; - g_object_notify (G_OBJECT (cellprogress), "pulse"); - } - - priv->pulse = pulse; - recompute_label (cellprogress); -} - -static void -gtk_cell_renderer_progress_finalize (GObject *object) -{ - GtkCellRendererProgress *cellprogress = GTK_CELL_RENDERER_PROGRESS (object); - GtkCellRendererProgressPrivate *priv = gtk_cell_renderer_progress_get_instance_private (cellprogress); - - g_free (priv->text); - g_free (priv->label); - - G_OBJECT_CLASS (gtk_cell_renderer_progress_parent_class)->finalize (object); -} - -static void -gtk_cell_renderer_progress_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec) -{ - GtkCellRendererProgress *cellprogress = GTK_CELL_RENDERER_PROGRESS (object); - GtkCellRendererProgressPrivate *priv = gtk_cell_renderer_progress_get_instance_private (cellprogress); - - switch (param_id) - { - case PROP_VALUE: - g_value_set_int (value, priv->value); - break; - case PROP_TEXT: - g_value_set_string (value, priv->text); - break; - case PROP_PULSE: - g_value_set_int (value, priv->pulse); - break; - case PROP_TEXT_XALIGN: - g_value_set_float (value, priv->text_xalign); - break; - case PROP_TEXT_YALIGN: - g_value_set_float (value, priv->text_yalign); - break; - case PROP_ORIENTATION: - g_value_set_enum (value, priv->orientation); - break; - case PROP_INVERTED: - g_value_set_boolean (value, priv->inverted); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); - } -} - -static void -gtk_cell_renderer_progress_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec) -{ - GtkCellRendererProgress *cellprogress = GTK_CELL_RENDERER_PROGRESS (object); - GtkCellRendererProgressPrivate *priv = gtk_cell_renderer_progress_get_instance_private (cellprogress); - - switch (param_id) - { - case PROP_VALUE: - gtk_cell_renderer_progress_set_value (cellprogress, - g_value_get_int (value)); - break; - case PROP_TEXT: - gtk_cell_renderer_progress_set_text (cellprogress, - g_value_get_string (value)); - break; - case PROP_PULSE: - gtk_cell_renderer_progress_set_pulse (cellprogress, - g_value_get_int (value)); - break; - case PROP_TEXT_XALIGN: - priv->text_xalign = g_value_get_float (value); - break; - case PROP_TEXT_YALIGN: - priv->text_yalign = g_value_get_float (value); - break; - case PROP_ORIENTATION: - if (priv->orientation != g_value_get_enum (value)) - { - priv->orientation = g_value_get_enum (value); - g_object_notify_by_pspec (object, pspec); - } - break; - case PROP_INVERTED: - if (priv->inverted != g_value_get_boolean (value)) - { - priv->inverted = g_value_get_boolean (value); - g_object_notify_by_pspec (object, pspec); - } - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); - } -} - -static void -compute_dimensions (GtkCellRenderer *cell, - GtkWidget *widget, - const char *text, - int *width, - int *height) -{ - PangoRectangle logical_rect; - PangoLayout *layout; - int xpad, ypad; - - layout = gtk_widget_create_pango_layout (widget, text); - pango_layout_get_pixel_extents (layout, NULL, &logical_rect); - - gtk_cell_renderer_get_padding (cell, &xpad, &ypad); - - if (width) - *width = logical_rect.width + xpad * 2; - - if (height) - *height = logical_rect.height + ypad * 2; - - g_object_unref (layout); -} - -static void -gtk_cell_renderer_progress_get_preferred_width (GtkCellRenderer *cell, - GtkWidget *widget, - int *minimum, - int *natural) -{ - GtkCellRendererProgress *self = GTK_CELL_RENDERER_PROGRESS (cell); - GtkCellRendererProgressPrivate *priv = gtk_cell_renderer_progress_get_instance_private (self); - int w, h; - int size; - - if (priv->min_w < 0) - { - char *text = g_strdup_printf (C_("progress bar label", "%d %%"), 100); - compute_dimensions (cell, widget, text, - &priv->min_w, - &priv->min_h); - g_free (text); - } - - compute_dimensions (cell, widget, priv->label, &w, &h); - - size = MAX (priv->min_w, w); - - if (minimum != NULL) - *minimum = size; - if (natural != NULL) - *natural = size; -} - -static void -gtk_cell_renderer_progress_get_preferred_height (GtkCellRenderer *cell, - GtkWidget *widget, - int *minimum, - int *natural) -{ - GtkCellRendererProgress *self = GTK_CELL_RENDERER_PROGRESS (cell); - GtkCellRendererProgressPrivate *priv = gtk_cell_renderer_progress_get_instance_private (self); - int w, h; - int size; - - if (priv->min_w < 0) - { - char *text = g_strdup_printf (C_("progress bar label", "%d %%"), 100); - compute_dimensions (cell, widget, text, - &priv->min_w, - &priv->min_h); - g_free (text); - } - - compute_dimensions (cell, widget, priv->label, &w, &h); - - size = MIN (priv->min_h, h); - - if (minimum != NULL) - *minimum = size; - if (natural != NULL) - *natural = size; -} - -static inline int -get_bar_size (int pulse, - int value, - int full_size) -{ - int bar_size; - - if (pulse < 0) - bar_size = full_size * MAX (0, value) / 100; - else if (pulse == 0) - bar_size = 0; - else if (pulse == G_MAXINT) - bar_size = full_size; - else - bar_size = MAX (2, full_size / 5); - - return bar_size; -} - -static inline int -get_bar_position (int start, - int full_size, - int bar_size, - int pulse, - int offset, - gboolean is_rtl) -{ - int position; - - if (pulse < 0 || pulse == 0 || pulse == G_MAXINT) - { - position = is_rtl ? (start + full_size - bar_size) : start; - } - else - { - position = (is_rtl ? offset + 12 : offset) % 24; - if (position > 12) - position = 24 - position; - position = start + full_size * position / 15; - } - - return position; -} - -static void -gtk_cell_renderer_progress_snapshot (GtkCellRenderer *cell, - GtkSnapshot *snapshot, - GtkWidget *widget, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - GtkCellRendererState flags) -{ - GtkCellRendererProgress *cellprogress = GTK_CELL_RENDERER_PROGRESS (cell); - GtkCellRendererProgressPrivate *priv = gtk_cell_renderer_progress_get_instance_private (cellprogress); - GtkStyleContext *context; - GtkBorder padding; - PangoLayout *layout; - PangoRectangle logical_rect; - int x, y, w, h, x_pos, y_pos, bar_position, bar_size, start, full_size; - int xpad, ypad; - GdkRectangle clip; - gboolean is_rtl; - - context = gtk_widget_get_style_context (widget); - is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL; - - gtk_cell_renderer_get_padding (cell, &xpad, &ypad); - x = cell_area->x + xpad; - y = cell_area->y + ypad; - w = cell_area->width - xpad * 2; - h = cell_area->height - ypad * 2; - - gtk_style_context_save (context); - gtk_style_context_add_class (context, "trough"); - - gtk_snapshot_render_background (snapshot, context, x, y, w, h); - gtk_snapshot_render_frame (snapshot, context, x, y, w, h); - - gtk_style_context_get_padding (context, &padding); - - x += padding.left; - y += padding.top; - w -= padding.left + padding.right; - h -= padding.top + padding.bottom; - - gtk_style_context_restore (context); - - if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) - { - clip.y = y; - clip.height = h; - - start = x; - full_size = w; - - bar_size = get_bar_size (priv->pulse, priv->value, full_size); - - if (!priv->inverted) - bar_position = get_bar_position (start, full_size, bar_size, - priv->pulse, priv->offset, is_rtl); - else - bar_position = get_bar_position (start, full_size, bar_size, - priv->pulse, priv->offset, !is_rtl); - - clip.width = bar_size; - clip.x = bar_position; - } - else - { - clip.x = x; - clip.width = w; - - start = y; - full_size = h; - - bar_size = get_bar_size (priv->pulse, priv->value, full_size); - - if (priv->inverted) - bar_position = get_bar_position (start, full_size, bar_size, - priv->pulse, priv->offset, TRUE); - else - bar_position = get_bar_position (start, full_size, bar_size, - priv->pulse, priv->offset, FALSE); - - clip.height = bar_size; - clip.y = bar_position; - } - - if (bar_size > 0) - { - gtk_style_context_save (context); - gtk_style_context_add_class (context, "progressbar"); - - gtk_snapshot_render_background (snapshot, context, clip.x, clip.y, clip.width, clip.height); - gtk_snapshot_render_frame (snapshot, context, clip.x, clip.y, clip.width, clip.height); - - gtk_style_context_restore (context); - } - - if (priv->label) - { - float text_xalign; - - layout = gtk_widget_create_pango_layout (widget, priv->label); - pango_layout_get_pixel_extents (layout, NULL, &logical_rect); - - if (gtk_widget_get_direction (widget) != GTK_TEXT_DIR_LTR) - text_xalign = 1.0 - priv->text_xalign; - else - text_xalign = priv->text_xalign; - - x_pos = x + padding.left + text_xalign * - (w - padding.left - padding.right - logical_rect.width); - - y_pos = y + padding.top + priv->text_yalign * - (h - padding.top - padding.bottom - logical_rect.height); - - gtk_snapshot_push_clip (snapshot, - &GRAPHENE_RECT_INIT( - clip.x, clip.y, - clip.width, clip.height - )); - - gtk_style_context_save (context); - gtk_style_context_add_class (context, "progressbar"); - - gtk_snapshot_render_layout (snapshot, context, - x_pos, y_pos, - layout); - - gtk_style_context_restore (context); - gtk_snapshot_pop (snapshot); - - gtk_style_context_save (context); - gtk_style_context_add_class (context, "trough"); - - if (bar_position > start) - { - if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) - { - clip.x = x; - clip.width = bar_position - x; - } - else - { - clip.y = y; - clip.height = bar_position - y; - } - - gtk_snapshot_push_clip (snapshot, - &GRAPHENE_RECT_INIT( - clip.x, clip.y, - clip.width, clip.height - )); - - gtk_snapshot_render_layout (snapshot, context, - x_pos, y_pos, - layout); - - gtk_snapshot_pop (snapshot); - } - - if (bar_position + bar_size < start + full_size) - { - if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) - { - clip.x = bar_position + bar_size; - clip.width = x + w - (bar_position + bar_size); - } - else - { - clip.y = bar_position + bar_size; - clip.height = y + h - (bar_position + bar_size); - } - - gtk_snapshot_push_clip (snapshot, - &GRAPHENE_RECT_INIT( - clip.x, clip.y, - clip.width, clip.height - )); - - gtk_snapshot_render_layout (snapshot, context, - x_pos, y_pos, - layout); - - gtk_snapshot_pop (snapshot); - } - - gtk_style_context_restore (context); - g_object_unref (layout); - } -} - -static void -gtk_cell_renderer_progress_class_init (GtkCellRendererProgressClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass); - - object_class->finalize = gtk_cell_renderer_progress_finalize; - object_class->get_property = gtk_cell_renderer_progress_get_property; - object_class->set_property = gtk_cell_renderer_progress_set_property; - - cell_class->get_preferred_width = gtk_cell_renderer_progress_get_preferred_width; - cell_class->get_preferred_height = gtk_cell_renderer_progress_get_preferred_height; - cell_class->snapshot = gtk_cell_renderer_progress_snapshot; - - /** - * GtkCellRendererProgress:value: - * - * The "value" property determines the percentage to which the - * progress bar will be "filled in". - **/ - g_object_class_install_property (object_class, - PROP_VALUE, - g_param_spec_int ("value", NULL, NULL, - 0, 100, 0, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - /** - * GtkCellRendererProgress:text: - * - * The "text" property determines the label which will be drawn - * over the progress bar. Setting this property to %NULL causes the default - * label to be displayed. Setting this property to an empty string causes - * no label to be displayed. - **/ - g_object_class_install_property (object_class, - PROP_TEXT, - g_param_spec_string ("text", NULL, NULL, - NULL, - GTK_PARAM_READWRITE)); - - /** - * GtkCellRendererProgress:pulse: - * - * Setting this to a non-negative value causes the cell renderer to - * enter "activity mode", where a block bounces back and forth to - * indicate that some progress is made, without specifying exactly how - * much. - * - * Each increment of the property causes the block to move by a little - * bit. - * - * To indicate that the activity has not started yet, set the property - * to zero. To indicate completion, set the property to %G_MAXINT. - */ - g_object_class_install_property (object_class, - PROP_PULSE, - g_param_spec_int ("pulse", NULL, NULL, - -1, G_MAXINT, -1, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - /** - * GtkCellRendererProgress:text-xalign: - * - * The "text-xalign" property controls the horizontal alignment of the - * text in the progress bar. Valid values range from 0 (left) to 1 - * (right). Reserved for RTL layouts. - */ - g_object_class_install_property (object_class, - PROP_TEXT_XALIGN, - g_param_spec_float ("text-xalign", NULL, NULL, - 0.0, 1.0, 0.5, - GTK_PARAM_READWRITE)); - - /** - * GtkCellRendererProgress:text-yalign: - * - * The "text-yalign" property controls the vertical alignment of the - * text in the progress bar. Valid values range from 0 (top) to 1 - * (bottom). - */ - g_object_class_install_property (object_class, - PROP_TEXT_YALIGN, - g_param_spec_float ("text-yalign", NULL, NULL, - 0.0, 1.0, 0.5, - GTK_PARAM_READWRITE)); - - g_object_class_override_property (object_class, - PROP_ORIENTATION, - "orientation"); - - g_object_class_install_property (object_class, - PROP_INVERTED, - g_param_spec_boolean ("inverted", NULL, NULL, - FALSE, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); -} - -static void -gtk_cell_renderer_progress_init (GtkCellRendererProgress *cellprogress) -{ - GtkCellRendererProgressPrivate *priv = gtk_cell_renderer_progress_get_instance_private (cellprogress); - - priv->value = 0; - priv->text = NULL; - priv->label = NULL; - priv->min_w = -1; - priv->min_h = -1; - priv->pulse = -1; - priv->offset = 0; - - priv->text_xalign = 0.5; - priv->text_yalign = 0.5; - - priv->orientation = GTK_ORIENTATION_HORIZONTAL, - priv->inverted = FALSE; -} - -/** - * gtk_cell_renderer_progress_new: - * - * Creates a new `GtkCellRendererProgress`. - * - * Returns: the new cell renderer - **/ -GtkCellRenderer* -gtk_cell_renderer_progress_new (void) -{ - return g_object_new (GTK_TYPE_CELL_RENDERER_PROGRESS, NULL); -} diff --git a/gtk/gtkcellrendererprogress.h b/gtk/gtkcellrendererprogress.h deleted file mode 100644 index b1a9c04e3e..0000000000 --- a/gtk/gtkcellrendererprogress.h +++ /dev/null @@ -1,52 +0,0 @@ -/* gtkcellrendererprogress.h - * Copyright (C) 2002 Naba Kumar - * modified by Jörgen Scheibengruber - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -/* - * Modified by the GTK+ Team and others 1997-2004. See the AUTHORS - * file for a list of people on the GTK+ Team. See the ChangeLog - * files for a list of changes. These files are distributed with - * GTK+ at ftp://ftp.gtk.org/pub/gtk/. - */ - -#ifndef __GTK_CELL_RENDERER_PROGRESS_H__ -#define __GTK_CELL_RENDERER_PROGRESS_H__ - -#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) -#error "Only can be included directly." -#endif - -#include - -G_BEGIN_DECLS - -#define GTK_TYPE_CELL_RENDERER_PROGRESS (gtk_cell_renderer_progress_get_type ()) -#define GTK_CELL_RENDERER_PROGRESS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_RENDERER_PROGRESS, GtkCellRendererProgress)) -#define GTK_IS_CELL_RENDERER_PROGRESS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_RENDERER_PROGRESS)) - -typedef struct _GtkCellRendererProgress GtkCellRendererProgress; - -GDK_AVAILABLE_IN_ALL -GType gtk_cell_renderer_progress_get_type (void) G_GNUC_CONST; -GDK_AVAILABLE_IN_ALL -GtkCellRenderer* gtk_cell_renderer_progress_new (void); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkCellRendererProgress, g_object_unref) - -G_END_DECLS - -#endif /* __GTK_CELL_RENDERER_PROGRESS_H__ */ diff --git a/gtk/gtkcellrendererspin.c b/gtk/gtkcellrendererspin.c deleted file mode 100644 index 92a12e6010..0000000000 --- a/gtk/gtkcellrendererspin.c +++ /dev/null @@ -1,376 +0,0 @@ -/* GtkCellRendererSpin - * Copyright (C) 2004 Lorenzo Gil Sanchez - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see . - * - * Authors: Lorenzo Gil Sanchez - * Carlos Garnacho Parro - */ - -#include "config.h" - -#include "gtkcellrendererspin.h" - -#include "gtkadjustment.h" -#include "gtkprivate.h" -#include "gtkspinbutton.h" -#include "gtkentry.h" -#include "gtkeventcontrollerkey.h" - - -/** - * GtkCellRendererSpin: - * - * Renders a spin button in a cell - * - * `GtkCellRendererSpin` renders text in a cell like `GtkCellRendererText` from - * which it is derived. But while `GtkCellRendererText` offers a simple entry to - * edit the text, `GtkCellRendererSpin` offers a `GtkSpinButton` widget. Of course, - * that means that the text has to be parseable as a floating point number. - * - * The range of the spinbutton is taken from the adjustment property of the - * cell renderer, which can be set explicitly or mapped to a column in the - * tree model, like all properties of cell renders. `GtkCellRendererSpin` - * also has properties for the `GtkCellRendererSpin:climb-rate` and the number - * of `GtkCellRendererSpin:digits` to display. Other `GtkSpinButton` properties - * can be set in a handler for the `GtkCellRenderer::editing-started` signal. - * - * The `GtkCellRendererSpin` cell renderer was added in GTK 2.10. - */ - -typedef struct _GtkCellRendererSpinClass GtkCellRendererSpinClass; -typedef struct _GtkCellRendererSpinPrivate GtkCellRendererSpinPrivate; - -struct _GtkCellRendererSpin -{ - GtkCellRendererText parent; -}; - -struct _GtkCellRendererSpinClass -{ - GtkCellRendererTextClass parent; -}; - -struct _GtkCellRendererSpinPrivate -{ - GtkWidget *spin; - GtkAdjustment *adjustment; - double climb_rate; - guint digits; -}; - -static void gtk_cell_renderer_spin_finalize (GObject *object); - -static void gtk_cell_renderer_spin_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *spec); -static void gtk_cell_renderer_spin_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *spec); - -static gboolean gtk_cell_renderer_spin_key_pressed (GtkEventControllerKey *controller, - guint keyval, - guint keycode, - GdkModifierType state, - GtkWidget *widget); - -static GtkCellEditable * gtk_cell_renderer_spin_start_editing (GtkCellRenderer *cell, - GdkEvent *event, - GtkWidget *widget, - const char *path, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - GtkCellRendererState flags); -enum { - PROP_0, - PROP_ADJUSTMENT, - PROP_CLIMB_RATE, - PROP_DIGITS -}; - -#define GTK_CELL_RENDERER_SPIN_PATH "gtk-cell-renderer-spin-path" - -G_DEFINE_TYPE_WITH_PRIVATE (GtkCellRendererSpin, gtk_cell_renderer_spin, GTK_TYPE_CELL_RENDERER_TEXT) - - -static void -gtk_cell_renderer_spin_class_init (GtkCellRendererSpinClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass); - - object_class->finalize = gtk_cell_renderer_spin_finalize; - object_class->get_property = gtk_cell_renderer_spin_get_property; - object_class->set_property = gtk_cell_renderer_spin_set_property; - - cell_class->start_editing = gtk_cell_renderer_spin_start_editing; - - /** - * GtkCellRendererSpin:adjustment: - * - * The adjustment that holds the value of the spinbutton. - * This must be non-%NULL for the cell renderer to be editable. - */ - g_object_class_install_property (object_class, - PROP_ADJUSTMENT, - g_param_spec_object ("adjustment", NULL, NULL, - GTK_TYPE_ADJUSTMENT, - GTK_PARAM_READWRITE)); - - - /** - * GtkCellRendererSpin:climb-rate: - * - * The acceleration rate when you hold down a button. - */ - g_object_class_install_property (object_class, - PROP_CLIMB_RATE, - g_param_spec_double ("climb-rate", NULL, NULL, - 0.0, G_MAXDOUBLE, 0.0, - GTK_PARAM_READWRITE)); - /** - * GtkCellRendererSpin:digits: - * - * The number of decimal places to display. - */ - g_object_class_install_property (object_class, - PROP_DIGITS, - g_param_spec_uint ("digits", NULL, NULL, - 0, 20, 0, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); -} - -static void -gtk_cell_renderer_spin_init (GtkCellRendererSpin *self) -{ - GtkCellRendererSpinPrivate *priv = gtk_cell_renderer_spin_get_instance_private (self); - - priv->adjustment = NULL; - priv->climb_rate = 0.0; - priv->digits = 0; -} - -static void -gtk_cell_renderer_spin_finalize (GObject *object) -{ - GtkCellRendererSpinPrivate *priv = gtk_cell_renderer_spin_get_instance_private (GTK_CELL_RENDERER_SPIN (object)); - - g_clear_object (&priv->adjustment); - g_clear_object (&priv->spin); - - G_OBJECT_CLASS (gtk_cell_renderer_spin_parent_class)->finalize (object); -} - -static void -gtk_cell_renderer_spin_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GtkCellRendererSpinPrivate *priv = gtk_cell_renderer_spin_get_instance_private (GTK_CELL_RENDERER_SPIN (object)); - - switch (prop_id) - { - case PROP_ADJUSTMENT: - g_value_set_object (value, priv->adjustment); - break; - case PROP_CLIMB_RATE: - g_value_set_double (value, priv->climb_rate); - break; - case PROP_DIGITS: - g_value_set_uint (value, priv->digits); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gtk_cell_renderer_spin_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - GtkCellRendererSpinPrivate *priv = gtk_cell_renderer_spin_get_instance_private (GTK_CELL_RENDERER_SPIN (object)); - GObject *obj; - - switch (prop_id) - { - case PROP_ADJUSTMENT: - obj = g_value_get_object (value); - - if (priv->adjustment) - { - g_object_unref (priv->adjustment); - priv->adjustment = NULL; - } - - if (obj) - priv->adjustment = GTK_ADJUSTMENT (g_object_ref_sink (obj)); - - break; - case PROP_CLIMB_RATE: - priv->climb_rate = g_value_get_double (value); - break; - case PROP_DIGITS: - if (priv->digits != g_value_get_uint (value)) - { - priv->digits = g_value_get_uint (value); - g_object_notify_by_pspec (object, pspec); - } - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gtk_cell_renderer_spin_focus_changed (GtkWidget *widget, - GParamSpec *pspec, - gpointer data) -{ - const char *path; - const char *new_text; - gboolean canceled; - - if (gtk_widget_has_focus (widget)) - return; - - g_object_get (widget, "editing-canceled", &canceled, NULL); - - g_signal_handlers_disconnect_by_func (widget, - gtk_cell_renderer_spin_focus_changed, - data); - - gtk_cell_renderer_stop_editing (GTK_CELL_RENDERER (data), canceled); - - if (canceled) - return; - - path = g_object_get_data (G_OBJECT (widget), GTK_CELL_RENDERER_SPIN_PATH); - new_text = gtk_editable_get_text (GTK_EDITABLE (widget)); - g_signal_emit_by_name (data, "edited", path, new_text); -} - -static gboolean -gtk_cell_renderer_spin_key_pressed (GtkEventControllerKey *controller, - guint keyval, - guint keycode, - GdkModifierType state, - GtkWidget *widget) -{ - if (state == 0) - { - if (keyval == GDK_KEY_Up) - { - gtk_spin_button_spin (GTK_SPIN_BUTTON (widget), GTK_SPIN_STEP_FORWARD, 1); - return TRUE; - } - else if (keyval == GDK_KEY_Down) - { - gtk_spin_button_spin (GTK_SPIN_BUTTON (widget), GTK_SPIN_STEP_BACKWARD, 1); - return TRUE; - } - } - - return FALSE; -} - -static void -gtk_cell_renderer_spin_editing_done (GtkSpinButton *spin, - GtkCellRendererSpin *cell) -{ - GtkCellRendererSpinPrivate *priv = gtk_cell_renderer_spin_get_instance_private (GTK_CELL_RENDERER_SPIN (cell)); - gboolean canceled; - const char *path; - const char *new_text; - - g_clear_object (&priv->spin); - - g_object_get (spin, "editing-canceled", &canceled, NULL); - gtk_cell_renderer_stop_editing (GTK_CELL_RENDERER (cell), canceled); - - if (canceled) - return; - - path = g_object_get_data (G_OBJECT (spin), GTK_CELL_RENDERER_SPIN_PATH); - new_text = gtk_editable_get_text (GTK_EDITABLE (spin)); - g_signal_emit_by_name (cell, "edited", path, new_text); -} - -static GtkCellEditable * -gtk_cell_renderer_spin_start_editing (GtkCellRenderer *cell, - GdkEvent *event, - GtkWidget *widget, - const char *path, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - GtkCellRendererState flags) -{ - GtkCellRendererSpinPrivate *priv = gtk_cell_renderer_spin_get_instance_private (GTK_CELL_RENDERER_SPIN (cell)); - GtkCellRendererText *cell_text = GTK_CELL_RENDERER_TEXT (cell); - GtkEventController *key_controller; - gboolean editable; - char *text; - - g_object_get (cell_text, "editable", &editable, NULL); - if (!editable) - return NULL; - - if (!priv->adjustment) - return NULL; - - priv->spin = gtk_spin_button_new (priv->adjustment, priv->climb_rate, priv->digits); - g_object_ref_sink (priv->spin); - - g_object_get (cell_text, "text", &text, NULL); - if (text) - { - gtk_spin_button_set_value (GTK_SPIN_BUTTON (priv->spin), g_strtod (text, NULL)); - g_free (text); - } - - key_controller = gtk_event_controller_key_new (); - g_signal_connect (key_controller, "key-pressed", - G_CALLBACK (gtk_cell_renderer_spin_key_pressed), priv->spin); - gtk_widget_add_controller (priv->spin, key_controller); - - g_object_set_data_full (G_OBJECT (priv->spin), GTK_CELL_RENDERER_SPIN_PATH, - g_strdup (path), g_free); - - g_signal_connect (priv->spin, "editing-done", - G_CALLBACK (gtk_cell_renderer_spin_editing_done), cell); - - g_signal_connect (priv->spin, "notify::has-focus", - G_CALLBACK (gtk_cell_renderer_spin_focus_changed), cell); - - return GTK_CELL_EDITABLE (priv->spin); -} - -/** - * gtk_cell_renderer_spin_new: - * - * Creates a new `GtkCellRendererSpin`. - * - * Returns: a new `GtkCellRendererSpin` - */ -GtkCellRenderer * -gtk_cell_renderer_spin_new (void) -{ - return g_object_new (GTK_TYPE_CELL_RENDERER_SPIN, NULL); -} diff --git a/gtk/gtkcellrendererspin.h b/gtk/gtkcellrendererspin.h deleted file mode 100644 index 5f94cc4b47..0000000000 --- a/gtk/gtkcellrendererspin.h +++ /dev/null @@ -1,44 +0,0 @@ -/* GtkCellRendererSpin - * Copyright (C) 2004 Lorenzo Gil Sanchez - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see . - */ - -#ifndef __GTK_CELL_RENDERER_SPIN_H__ -#define __GTK_CELL_RENDERER_SPIN_H__ - -#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) -#error "Only can be included directly." -#endif - -#include - -G_BEGIN_DECLS - -#define GTK_TYPE_CELL_RENDERER_SPIN (gtk_cell_renderer_spin_get_type ()) -#define GTK_CELL_RENDERER_SPIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_RENDERER_SPIN, GtkCellRendererSpin)) -#define GTK_IS_CELL_RENDERER_SPIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_RENDERER_SPIN)) - -typedef struct _GtkCellRendererSpin GtkCellRendererSpin; - -GDK_AVAILABLE_IN_ALL -GType gtk_cell_renderer_spin_get_type (void); -GDK_AVAILABLE_IN_ALL -GtkCellRenderer *gtk_cell_renderer_spin_new (void); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkCellRendererSpin, g_object_unref) - -G_END_DECLS - -#endif /* __GTK_CELL_RENDERER_SPIN_H__ */ diff --git a/gtk/gtkcellrendererspinner.c b/gtk/gtkcellrendererspinner.c deleted file mode 100644 index c7d0d12822..0000000000 --- a/gtk/gtkcellrendererspinner.c +++ /dev/null @@ -1,488 +0,0 @@ -/* GTK - The GIMP Toolkit - * - * Copyright (C) 2009 Matthias Clasen - * Copyright (C) 2008 Richard Hughes - * Copyright (C) 2009 Bastien Nocera - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see . - */ - -/* - * Modified by the GTK+ Team and others 2007. See the AUTHORS - * file for a list of people on the GTK+ Team. See the ChangeLog - * files for a list of changes. These files are distributed with - * GTK+ at ftp://ftp.gtk.org/pub/gtk/. - */ - -#include "config.h" - -#include "gtkcellrendererspinner.h" -#include "gtkiconhelperprivate.h" -#include "gtksettings.h" -#include "gtksnapshot.h" -#include "gtktypebuiltins.h" -#include "gtkstylecontextprivate.h" -#include "gtkcssnumbervalueprivate.h" - -#include - -/** - * GtkCellRendererSpinner: - * - * Renders a spinning animation in a cell - * - * `GtkCellRendererSpinner` renders a spinning animation in a cell, very - * similar to `GtkSpinner`. It can often be used as an alternative - * to a `GtkCellRendererProgress` for displaying indefinite activity, - * instead of actual progress. - * - * To start the animation in a cell, set the `GtkCellRendererSpinner:active` - * property to %TRUE and increment the `GtkCellRendererSpinner:pulse` property - * at regular intervals. The usual way to set the cell renderer properties - * for each cell is to bind them to columns in your tree model using e.g. - * gtk_tree_view_column_add_attribute(). - */ - - -enum { - PROP_0, - PROP_ACTIVE, - PROP_PULSE, - PROP_SIZE -}; - -typedef struct _GtkCellRendererSpinnerClass GtkCellRendererSpinnerClass; -typedef struct _GtkCellRendererSpinnerPrivate GtkCellRendererSpinnerPrivate; - -struct _GtkCellRendererSpinner -{ - GtkCellRenderer parent; -}; - -struct _GtkCellRendererSpinnerClass -{ - GtkCellRendererClass parent_class; - - /* Padding for future expansion */ - void (*_gtk_reserved1) (void); - void (*_gtk_reserved2) (void); - void (*_gtk_reserved3) (void); - void (*_gtk_reserved4) (void); -}; - -struct _GtkCellRendererSpinnerPrivate -{ - gboolean active; - guint pulse; - GtkIconSize icon_size; - int size; -}; - - -static void gtk_cell_renderer_spinner_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec); -static void gtk_cell_renderer_spinner_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec); -static void gtk_cell_renderer_spinner_get_size (GtkCellRendererSpinner *self, - GtkWidget *widget, - const GdkRectangle *cell_area, - int *x_offset, - int *y_offset, - int *width, - int *height); -static void gtk_cell_renderer_spinner_snapshot (GtkCellRenderer *cell, - GtkSnapshot *snapshot, - GtkWidget *widget, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - GtkCellRendererState flags); - -G_DEFINE_TYPE_WITH_PRIVATE (GtkCellRendererSpinner, gtk_cell_renderer_spinner, GTK_TYPE_CELL_RENDERER) - -static GtkSizeRequestMode -gtk_cell_renderer_spinner_get_request_mode (GtkCellRenderer *cell) -{ - return GTK_SIZE_REQUEST_CONSTANT_SIZE; -} - -static void -gtk_cell_renderer_spinner_get_preferred_width (GtkCellRenderer *cell, - GtkWidget *widget, - int *minimum, - int *natural) -{ - int size = 0; - - gtk_cell_renderer_spinner_get_size (GTK_CELL_RENDERER_SPINNER (cell), widget, - NULL, - NULL, NULL, &size, NULL); - - if (minimum != NULL) - *minimum = size; - if (natural != NULL) - *natural = size; -} - -static void -gtk_cell_renderer_spinner_get_preferred_height (GtkCellRenderer *cell, - GtkWidget *widget, - int *minimum, - int *natural) -{ - int size = 0; - - gtk_cell_renderer_spinner_get_size (GTK_CELL_RENDERER_SPINNER (cell), widget, - NULL, - NULL, NULL, NULL, &size); - - if (minimum != NULL) - *minimum = size; - if (natural != NULL) - *natural = size; -} - -static void -gtk_cell_renderer_spinner_class_init (GtkCellRendererSpinnerClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass); - - object_class->get_property = gtk_cell_renderer_spinner_get_property; - object_class->set_property = gtk_cell_renderer_spinner_set_property; - - cell_class->get_request_mode = gtk_cell_renderer_spinner_get_request_mode; - cell_class->get_preferred_width = gtk_cell_renderer_spinner_get_preferred_width; - cell_class->get_preferred_height = gtk_cell_renderer_spinner_get_preferred_height; - cell_class->snapshot = gtk_cell_renderer_spinner_snapshot; - - /* GtkCellRendererSpinner:active: - * - * Whether the spinner is active (ie. shown) in the cell - */ - g_object_class_install_property (object_class, - PROP_ACTIVE, - g_param_spec_boolean ("active", NULL, NULL, - FALSE, - G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - /** - * GtkCellRendererSpinner:pulse: - * - * Pulse of the spinner. Increment this value to draw the next frame of the - * spinner animation. Usually, you would update this value in a timeout. - * - * By default, the `GtkSpinner` widget draws one full cycle of the animation, - * consisting of 12 frames, in 750 milliseconds. - */ - g_object_class_install_property (object_class, - PROP_PULSE, - g_param_spec_uint ("pulse", NULL, NULL, - 0, G_MAXUINT, 0, - G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - /** - * GtkCellRendererSpinner:size: - * - * The `GtkIconSize` value that specifies the size of the rendered spinner. - */ - g_object_class_install_property (object_class, - PROP_SIZE, - g_param_spec_enum ("size", NULL, NULL, - GTK_TYPE_ICON_SIZE, GTK_ICON_SIZE_INHERIT, - G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - -} - -static void -gtk_cell_renderer_spinner_init (GtkCellRendererSpinner *cell) -{ - GtkCellRendererSpinnerPrivate *priv = gtk_cell_renderer_spinner_get_instance_private (cell); - priv->pulse = 0; - priv->icon_size = GTK_ICON_SIZE_INHERIT; -} - -/** - * gtk_cell_renderer_spinner_new: - * - * Returns a new cell renderer which will show a spinner to indicate - * activity. - * - * Returns: a new `GtkCellRenderer` - */ -GtkCellRenderer * -gtk_cell_renderer_spinner_new (void) -{ - return g_object_new (GTK_TYPE_CELL_RENDERER_SPINNER, NULL); -} - -static void -gtk_cell_renderer_spinner_update_size (GtkCellRendererSpinner *cell, - GtkWidget *widget) -{ - GtkCellRendererSpinnerPrivate *priv = gtk_cell_renderer_spinner_get_instance_private (cell); - GtkStyleContext *context; - GtkCssNode *node; - GtkCssStyle *style; - - context = gtk_widget_get_style_context (widget); - gtk_style_context_save (context); - - gtk_style_context_add_class (context, "spinner"); - node = gtk_style_context_get_node (context); - gtk_icon_size_set_style_classes (node, priv->icon_size); - style = gtk_css_node_get_style (node); - priv->size = _gtk_css_number_value_get (style->icon->icon_size, 100); - - gtk_style_context_restore (context); -} - -static void -gtk_cell_renderer_spinner_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec) -{ - GtkCellRendererSpinner *cell = GTK_CELL_RENDERER_SPINNER (object); - GtkCellRendererSpinnerPrivate *priv = gtk_cell_renderer_spinner_get_instance_private (cell); - - switch (param_id) - { - case PROP_ACTIVE: - g_value_set_boolean (value, priv->active); - break; - case PROP_PULSE: - g_value_set_uint (value, priv->pulse); - break; - case PROP_SIZE: - g_value_set_enum (value, priv->icon_size); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); - } -} - -static void -gtk_cell_renderer_spinner_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec) -{ - GtkCellRendererSpinner *cell = GTK_CELL_RENDERER_SPINNER (object); - GtkCellRendererSpinnerPrivate *priv = gtk_cell_renderer_spinner_get_instance_private (cell); - - switch (param_id) - { - case PROP_ACTIVE: - if (priv->active != g_value_get_boolean (value)) - { - priv->active = g_value_get_boolean (value); - g_object_notify (object, "active"); - } - break; - case PROP_PULSE: - if (priv->pulse != g_value_get_uint (value)) - { - priv->pulse = g_value_get_uint (value); - g_object_notify (object, "pulse"); - } - break; - case PROP_SIZE: - if (priv->icon_size != g_value_get_enum (value)) - { - priv->icon_size = g_value_get_enum (value); - g_object_notify (object, "size"); - } - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); - } -} - -static void -gtk_cell_renderer_spinner_get_size (GtkCellRendererSpinner *self, - GtkWidget *widget, - const GdkRectangle *cell_area, - int *x_offset, - int *y_offset, - int *width, - int *height) -{ - GtkCellRendererSpinnerPrivate *priv = gtk_cell_renderer_spinner_get_instance_private (self); - double align; - int w, h; - int xpad, ypad; - float xalign, yalign; - gboolean rtl; - - rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL; - - gtk_cell_renderer_spinner_update_size (self, widget); - - g_object_get (self, - "xpad", &xpad, - "ypad", &ypad, - "xalign", &xalign, - "yalign", &yalign, - NULL); - - w = h = priv->size; - - if (cell_area) - { - if (x_offset) - { - align = rtl ? 1.0 - xalign : xalign; - *x_offset = align * (cell_area->width - w); - *x_offset = MAX (*x_offset, 0); - } - if (y_offset) - { - align = rtl ? 1.0 - yalign : yalign; - *y_offset = align * (cell_area->height - h); - *y_offset = MAX (*y_offset, 0); - } - } - else - { - if (x_offset) - *x_offset = 0; - if (y_offset) - *y_offset = 0; - } - - if (width) - *width = w; - if (height) - *height = h; -} - -static void -gtk_paint_spinner (GtkStyleContext *context, - cairo_t *cr, - guint step, - int x, - int y, - int width, - int height) -{ - GdkRGBA color; - guint num_steps; - double dx, dy; - double radius; - double half; - int i; - guint real_step; - - num_steps = 12; - real_step = step % num_steps; - - /* set a clip region for the expose event */ - cairo_rectangle (cr, x, y, width, height); - cairo_clip (cr); - - cairo_translate (cr, x, y); - - /* draw clip region */ - cairo_set_operator (cr, CAIRO_OPERATOR_OVER); - - gtk_style_context_get_color (context, &color); - dx = width / 2; - dy = height / 2; - radius = MIN (width / 2, height / 2); - half = num_steps / 2; - - for (i = 0; i < num_steps; i++) - { - int inset = 0.7 * radius; - - /* transparency is a function of time and initial value */ - double t = (double) ((i + num_steps - real_step) - % num_steps) / num_steps; - - cairo_save (cr); - - cairo_set_source_rgba (cr, - color.red / 65535., - color.green / 65535., - color.blue / 65535., - color.alpha * t); - - cairo_set_line_width (cr, 2.0); - cairo_move_to (cr, - dx + (radius - inset) * cos (i * G_PI / half), - dy + (radius - inset) * sin (i * G_PI / half)); - cairo_line_to (cr, - dx + radius * cos (i * G_PI / half), - dy + radius * sin (i * G_PI / half)); - cairo_stroke (cr); - - cairo_restore (cr); - } -} - -static void -gtk_cell_renderer_spinner_snapshot (GtkCellRenderer *cell, - GtkSnapshot *snapshot, - GtkWidget *widget, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - GtkCellRendererState flags) -{ - GtkCellRendererSpinner *self = GTK_CELL_RENDERER_SPINNER (cell); - GtkCellRendererSpinnerPrivate *priv = gtk_cell_renderer_spinner_get_instance_private (self); - GdkRectangle pix_rect; - GdkRectangle draw_rect; - int xpad, ypad; - cairo_t *cr; - - if (!priv->active) - return; - - gtk_cell_renderer_spinner_get_size (self, widget, cell_area, - &pix_rect.x, - &pix_rect.y, - &pix_rect.width, - &pix_rect.height); - - g_object_get (self, - "xpad", &xpad, - "ypad", &ypad, - NULL); - - pix_rect.x += cell_area->x + xpad; - pix_rect.y += cell_area->y + ypad; - pix_rect.width -= xpad * 2; - pix_rect.height -= ypad * 2; - - if (!gdk_rectangle_intersect (cell_area, &pix_rect, &draw_rect)) - return; - - cr = gtk_snapshot_append_cairo (snapshot, - &GRAPHENE_RECT_INIT ( - cell_area->x, cell_area->y, - cell_area->width, cell_area->height - )); - - gtk_paint_spinner (gtk_widget_get_style_context (widget), - cr, - priv->pulse, - draw_rect.x, draw_rect.y, - draw_rect.width, draw_rect.height); - - cairo_destroy (cr); -} diff --git a/gtk/gtkcellrendererspinner.h b/gtk/gtkcellrendererspinner.h deleted file mode 100644 index 68b701bb3b..0000000000 --- a/gtk/gtkcellrendererspinner.h +++ /dev/null @@ -1,47 +0,0 @@ -/* GTK - The GIMP Toolkit - * - * Copyright (C) 2009 Matthias Clasen - * Copyright (C) 2008 Richard Hughes - * Copyright (C) 2009 Bastien Nocera - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see . - */ - -#ifndef __GTK_CELL_RENDERER_SPINNER_H__ -#define __GTK_CELL_RENDERER_SPINNER_H__ - -#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) -#error "Only can be included directly." -#endif - -#include - -G_BEGIN_DECLS - -#define GTK_TYPE_CELL_RENDERER_SPINNER (gtk_cell_renderer_spinner_get_type ()) -#define GTK_CELL_RENDERER_SPINNER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_RENDERER_SPINNER, GtkCellRendererSpinner)) -#define GTK_IS_CELL_RENDERER_SPINNER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_RENDERER_SPINNER)) - -typedef struct _GtkCellRendererSpinner GtkCellRendererSpinner; - -GDK_AVAILABLE_IN_ALL -GType gtk_cell_renderer_spinner_get_type (void) G_GNUC_CONST; -GDK_AVAILABLE_IN_ALL -GtkCellRenderer *gtk_cell_renderer_spinner_new (void); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkCellRendererSpinner, g_object_unref) - -G_END_DECLS - -#endif /* __GTK_CELL_RENDERER_SPINNER_H__ */ diff --git a/gtk/gtkcellrenderertext.c b/gtk/gtkcellrenderertext.c deleted file mode 100644 index d2dedcd491..0000000000 --- a/gtk/gtkcellrenderertext.c +++ /dev/null @@ -1,1935 +0,0 @@ -/* gtkcellrenderertext.c - * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#include "config.h" - -#include "gtkcellrenderertext.h" - -#include "gtkcssnumbervalueprivate.h" -#include "gtkeditable.h" -#include "gtkentry.h" -#include "gtkentryprivate.h" -#include "gtkmarshalers.h" -#include "gtkprivate.h" -#include "gtksizerequest.h" -#include "gtksnapshot.h" -#include "gtkstylecontextprivate.h" -#include "gtktreeprivate.h" - -#include - -/** - * GtkCellRendererText: - * - * Renders text in a cell - * - * A `GtkCellRendererText` renders a given text in its cell, using the font, color and - * style information provided by its properties. The text will be ellipsized if it is - * too long and the `GtkCellRendererText:ellipsize` property allows it. - * - * If the `GtkCellRenderer:mode` is %GTK_CELL_RENDERER_MODE_EDITABLE, - * the `GtkCellRendererText` allows to edit its text using an entry. - */ - - -static void gtk_cell_renderer_text_finalize (GObject *object); - -static void gtk_cell_renderer_text_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec); -static void gtk_cell_renderer_text_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec); -static void gtk_cell_renderer_text_snapshot (GtkCellRenderer *cell, - GtkSnapshot *snapshot, - GtkWidget *widget, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - GtkCellRendererState flags); - -static GtkCellEditable *gtk_cell_renderer_text_start_editing (GtkCellRenderer *cell, - GdkEvent *event, - GtkWidget *widget, - const char *path, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - GtkCellRendererState flags); - -static void gtk_cell_renderer_text_get_preferred_width (GtkCellRenderer *cell, - GtkWidget *widget, - int *minimal_size, - int *natural_size); -static void gtk_cell_renderer_text_get_preferred_height (GtkCellRenderer *cell, - GtkWidget *widget, - int *minimal_size, - int *natural_size); -static void gtk_cell_renderer_text_get_preferred_height_for_width (GtkCellRenderer *cell, - GtkWidget *widget, - int width, - int *minimum_height, - int *natural_height); -static void gtk_cell_renderer_text_get_aligned_area (GtkCellRenderer *cell, - GtkWidget *widget, - GtkCellRendererState flags, - const GdkRectangle *cell_area, - GdkRectangle *aligned_area); - - - -enum { - EDITED, - LAST_SIGNAL -}; - -enum { - PROP_0, - - PROP_TEXT, - PROP_MARKUP, - PROP_ATTRIBUTES, - PROP_SINGLE_PARAGRAPH_MODE, - PROP_WIDTH_CHARS, - PROP_MAX_WIDTH_CHARS, - PROP_WRAP_WIDTH, - PROP_ALIGN, - PROP_PLACEHOLDER_TEXT, - - /* Style args */ - PROP_BACKGROUND, - PROP_FOREGROUND, - PROP_BACKGROUND_RGBA, - PROP_FOREGROUND_RGBA, - PROP_FONT, - PROP_FONT_DESC, - PROP_FAMILY, - PROP_STYLE, - PROP_VARIANT, - PROP_WEIGHT, - PROP_STRETCH, - PROP_SIZE, - PROP_SIZE_POINTS, - PROP_SCALE, - PROP_EDITABLE, - PROP_STRIKETHROUGH, - PROP_UNDERLINE, - PROP_RISE, - PROP_LANGUAGE, - PROP_ELLIPSIZE, - PROP_WRAP_MODE, - - /* Whether-a-style-arg-is-set args */ - PROP_BACKGROUND_SET, - PROP_FOREGROUND_SET, - PROP_FAMILY_SET, - PROP_STYLE_SET, - PROP_VARIANT_SET, - PROP_WEIGHT_SET, - PROP_STRETCH_SET, - PROP_SIZE_SET, - PROP_SCALE_SET, - PROP_EDITABLE_SET, - PROP_STRIKETHROUGH_SET, - PROP_UNDERLINE_SET, - PROP_RISE_SET, - PROP_LANGUAGE_SET, - PROP_ELLIPSIZE_SET, - PROP_ALIGN_SET, - - LAST_PROP -}; - -static guint text_cell_renderer_signals [LAST_SIGNAL]; -static GParamSpec *text_cell_renderer_props [LAST_PROP]; - -#define GTK_CELL_RENDERER_TEXT_PATH "gtk-cell-renderer-text-path" - -typedef struct _GtkCellRendererTextPrivate GtkCellRendererTextPrivate; - -struct _GtkCellRendererTextPrivate -{ - GtkWidget *entry; - - PangoAttrList *extra_attrs; - GdkRGBA foreground; - GdkRGBA background; - PangoAlignment align; - PangoEllipsizeMode ellipsize; - PangoFontDescription *font; - PangoLanguage *language; - PangoUnderline underline_style; - PangoWrapMode wrap_mode; - - char *text; - char *placeholder_text; - - double font_scale; - - int rise; - int fixed_height_rows; - int width_chars; - int max_width_chars; - int wrap_width; - - guint in_entry_menu : 1; - guint strikethrough : 1; - guint editable : 1; - guint scale_set : 1; - guint foreground_set : 1; - guint background_set : 1; - guint underline_set : 1; - guint rise_set : 1; - guint strikethrough_set : 1; - guint editable_set : 1; - guint calc_fixed_height : 1; - guint single_paragraph : 1; - guint language_set : 1; - guint markup_set : 1; - guint ellipsize_set : 1; - guint align_set : 1; - - gulong focus_out_id; - gulong entry_menu_popdown_timeout; -}; - -G_DEFINE_TYPE_WITH_PRIVATE (GtkCellRendererText, gtk_cell_renderer_text, GTK_TYPE_CELL_RENDERER) - -static void -gtk_cell_renderer_text_init (GtkCellRendererText *celltext) -{ - GtkCellRendererTextPrivate *priv = gtk_cell_renderer_text_get_instance_private (celltext); - GtkCellRenderer *cell = GTK_CELL_RENDERER (celltext); - - gtk_cell_renderer_set_alignment (cell, 0.0, 0.5); - gtk_cell_renderer_set_padding (cell, 2, 2); - priv->font_scale = 1.0; - priv->fixed_height_rows = -1; - priv->font = pango_font_description_new (); - - priv->width_chars = -1; - priv->max_width_chars = -1; - priv->wrap_width = -1; - priv->wrap_mode = PANGO_WRAP_CHAR; - priv->align = PANGO_ALIGN_LEFT; - priv->align_set = FALSE; -} - -static void -gtk_cell_renderer_text_class_init (GtkCellRendererTextClass *class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (class); - GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (class); - - object_class->finalize = gtk_cell_renderer_text_finalize; - - object_class->get_property = gtk_cell_renderer_text_get_property; - object_class->set_property = gtk_cell_renderer_text_set_property; - - cell_class->snapshot = gtk_cell_renderer_text_snapshot; - cell_class->start_editing = gtk_cell_renderer_text_start_editing; - cell_class->get_preferred_width = gtk_cell_renderer_text_get_preferred_width; - cell_class->get_preferred_height = gtk_cell_renderer_text_get_preferred_height; - cell_class->get_preferred_height_for_width = gtk_cell_renderer_text_get_preferred_height_for_width; - cell_class->get_aligned_area = gtk_cell_renderer_text_get_aligned_area; - - text_cell_renderer_props[PROP_TEXT] = - g_param_spec_string ("text", NULL, NULL, - NULL, - GTK_PARAM_READWRITE); - - text_cell_renderer_props[PROP_MARKUP] = - g_param_spec_string ("markup", NULL, NULL, - NULL, - GTK_PARAM_WRITABLE); - - text_cell_renderer_props[PROP_ATTRIBUTES] = - g_param_spec_boxed ("attributes", NULL, NULL, - PANGO_TYPE_ATTR_LIST, - GTK_PARAM_READWRITE); - - text_cell_renderer_props[PROP_SINGLE_PARAGRAPH_MODE] = - g_param_spec_boolean ("single-paragraph-mode", NULL, NULL, - FALSE, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); - - text_cell_renderer_props[PROP_BACKGROUND] = - g_param_spec_string ("background", NULL, NULL, - NULL, - GTK_PARAM_WRITABLE); - - /** - * GtkCellRendererText:background-rgba: - * - * Background color as a `GdkRGBA` - */ - text_cell_renderer_props[PROP_BACKGROUND_RGBA] = - g_param_spec_boxed ("background-rgba", NULL, NULL, - GDK_TYPE_RGBA, - GTK_PARAM_READWRITE); - text_cell_renderer_props[PROP_FOREGROUND] = - g_param_spec_string ("foreground", NULL, NULL, - NULL, - GTK_PARAM_WRITABLE); - - /** - * GtkCellRendererText:foreground-rgba: - * - * Foreground color as a `GdkRGBA` - */ - text_cell_renderer_props[PROP_FOREGROUND_RGBA] = - g_param_spec_boxed ("foreground-rgba", NULL, NULL, - GDK_TYPE_RGBA, - GTK_PARAM_READWRITE); - - - text_cell_renderer_props[PROP_EDITABLE] = - g_param_spec_boolean ("editable", NULL, NULL, - FALSE, - GTK_PARAM_READWRITE); - - text_cell_renderer_props[PROP_FONT] = - g_param_spec_string ("font", NULL, NULL, - NULL, - GTK_PARAM_READWRITE); - - text_cell_renderer_props[PROP_FONT_DESC] = - g_param_spec_boxed ("font-desc", NULL, NULL, - PANGO_TYPE_FONT_DESCRIPTION, - GTK_PARAM_READWRITE); - - text_cell_renderer_props[PROP_FAMILY] = - g_param_spec_string ("family", NULL, NULL, - NULL, - GTK_PARAM_READWRITE); - - text_cell_renderer_props[PROP_STYLE] = - g_param_spec_enum ("style", NULL, NULL, - PANGO_TYPE_STYLE, - PANGO_STYLE_NORMAL, - GTK_PARAM_READWRITE); - - text_cell_renderer_props[PROP_VARIANT] = - g_param_spec_enum ("variant", NULL, NULL, - PANGO_TYPE_VARIANT, - PANGO_VARIANT_NORMAL, - GTK_PARAM_READWRITE); - - text_cell_renderer_props[PROP_WEIGHT] = - g_param_spec_int ("weight", NULL, NULL, - 0, G_MAXINT, - PANGO_WEIGHT_NORMAL, - GTK_PARAM_READWRITE); - - text_cell_renderer_props[PROP_STRETCH] = - g_param_spec_enum ("stretch", NULL, NULL, - PANGO_TYPE_STRETCH, - PANGO_STRETCH_NORMAL, - GTK_PARAM_READWRITE); - - text_cell_renderer_props[PROP_SIZE] = - g_param_spec_int ("size", NULL, NULL, - 0, G_MAXINT, - 0, - GTK_PARAM_READWRITE); - - text_cell_renderer_props[PROP_SIZE_POINTS] = - g_param_spec_double ("size-points", NULL, NULL, - 0.0, G_MAXDOUBLE, - 0.0, - GTK_PARAM_READWRITE); - - text_cell_renderer_props[PROP_SCALE] = - g_param_spec_double ("scale", NULL, NULL, - 0.0, G_MAXDOUBLE, - 1.0, - GTK_PARAM_READWRITE); - - text_cell_renderer_props[PROP_RISE] = - g_param_spec_int ("rise", NULL, NULL, - -G_MAXINT, G_MAXINT, - 0, - GTK_PARAM_READWRITE); - - - text_cell_renderer_props[PROP_STRIKETHROUGH] = - g_param_spec_boolean ("strikethrough", NULL, NULL, - FALSE, - GTK_PARAM_READWRITE); - - text_cell_renderer_props[PROP_UNDERLINE] = - g_param_spec_enum ("underline", NULL, NULL, - PANGO_TYPE_UNDERLINE, - PANGO_UNDERLINE_NONE, - GTK_PARAM_READWRITE); - - text_cell_renderer_props[PROP_LANGUAGE] = - g_param_spec_string ("language", NULL, NULL, - NULL, - GTK_PARAM_READWRITE); - - /** - * GtkCellRendererText:ellipsize: - * - * Specifies the preferred place to ellipsize the string, if the cell renderer - * does not have enough room to display the entire string. Setting it to - * %PANGO_ELLIPSIZE_NONE turns off ellipsizing. See the wrap-width property - * for another way of making the text fit into a given width. - */ - text_cell_renderer_props[PROP_ELLIPSIZE] = - g_param_spec_enum ("ellipsize", NULL, NULL, - PANGO_TYPE_ELLIPSIZE_MODE, - PANGO_ELLIPSIZE_NONE, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); - - /** - * GtkCellRendererText:width-chars: - * - * The desired width of the cell, in characters. If this property is set to - * -1, the width will be calculated automatically, otherwise the cell will - * request either 3 characters or the property value, whichever is greater. - **/ - text_cell_renderer_props[PROP_WIDTH_CHARS] = - g_param_spec_int ("width-chars", NULL, NULL, - -1, G_MAXINT, - -1, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); - - /** - * GtkCellRendererText:max-width-chars: - * - * The desired maximum width of the cell, in characters. If this property - * is set to -1, the width will be calculated automatically. - * - * For cell renderers that ellipsize or wrap text; this property - * controls the maximum reported width of the cell. The - * cell should not receive any greater allocation unless it is - * set to expand in its `GtkCellLayout` and all of the cell's siblings - * have received their natural width. - **/ - text_cell_renderer_props[PROP_MAX_WIDTH_CHARS] = - g_param_spec_int ("max-width-chars", NULL, NULL, - -1, G_MAXINT, - -1, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); - - /** - * GtkCellRendererText:wrap-mode: - * - * Specifies how to break the string into multiple lines, if the cell - * renderer does not have enough room to display the entire string. - * This property has no effect unless the wrap-width property is set. - */ - text_cell_renderer_props[PROP_WRAP_MODE] = - g_param_spec_enum ("wrap-mode", NULL, NULL, - PANGO_TYPE_WRAP_MODE, - PANGO_WRAP_CHAR, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); - - /** - * GtkCellRendererText:wrap-width: - * - * Specifies the minimum width at which the text is wrapped. The wrap-mode property can - * be used to influence at what character positions the line breaks can be placed. - * Setting wrap-width to -1 turns wrapping off. - */ - text_cell_renderer_props[PROP_WRAP_WIDTH] = - g_param_spec_int ("wrap-width", NULL, NULL, - -1, G_MAXINT, - -1, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); - - /** - * GtkCellRendererText:alignment: - * - * Specifies how to align the lines of text with respect to each other. - * - * Note that this property describes how to align the lines of text in - * case there are several of them. The "xalign" property of `GtkCellRenderer`, - * on the other hand, sets the horizontal alignment of the whole text. - */ - text_cell_renderer_props[PROP_ALIGN] = - g_param_spec_enum ("alignment", NULL, NULL, - PANGO_TYPE_ALIGNMENT, - PANGO_ALIGN_LEFT, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); - - /** - * GtkCellRendererText:placeholder-text: - * - * The text that will be displayed in the `GtkCellRenderer` if - * `GtkCellRendererText:editable` is %TRUE and the cell is empty. - */ - text_cell_renderer_props[PROP_PLACEHOLDER_TEXT] = - g_param_spec_string ("placeholder-text", NULL, NULL, - NULL, - GTK_PARAM_READWRITE); - - /* Style props are set or not */ - -#define ADD_SET_PROP(propname, propval, nick, blurb) text_cell_renderer_props[propval] = g_param_spec_boolean (propname, nick, blurb, FALSE, GTK_PARAM_READWRITE) - - ADD_SET_PROP ("background-set", PROP_BACKGROUND_SET, NULL, NULL); - - ADD_SET_PROP ("foreground-set", PROP_FOREGROUND_SET, NULL, NULL); - - ADD_SET_PROP ("editable-set", PROP_EDITABLE_SET, NULL, NULL); - - ADD_SET_PROP ("family-set", PROP_FAMILY_SET, NULL, NULL); - - ADD_SET_PROP ("style-set", PROP_STYLE_SET, NULL, NULL); - - ADD_SET_PROP ("variant-set", PROP_VARIANT_SET, NULL, NULL); - - ADD_SET_PROP ("weight-set", PROP_WEIGHT_SET, NULL, NULL); - - ADD_SET_PROP ("stretch-set", PROP_STRETCH_SET, NULL, NULL); - - ADD_SET_PROP ("size-set", PROP_SIZE_SET, NULL, NULL); - - ADD_SET_PROP ("scale-set", PROP_SCALE_SET, NULL, NULL); - - ADD_SET_PROP ("rise-set", PROP_RISE_SET, NULL, NULL); - - ADD_SET_PROP ("strikethrough-set", PROP_STRIKETHROUGH_SET, NULL, NULL); - - ADD_SET_PROP ("underline-set", PROP_UNDERLINE_SET, NULL, NULL); - - ADD_SET_PROP ("language-set", PROP_LANGUAGE_SET, NULL, NULL); - - ADD_SET_PROP ("ellipsize-set", PROP_ELLIPSIZE_SET, NULL, NULL); - - ADD_SET_PROP ("align-set", PROP_ALIGN_SET, NULL, NULL); - - g_object_class_install_properties (object_class, LAST_PROP, text_cell_renderer_props); - - /** - * GtkCellRendererText::edited: - * @renderer: the object which received the signal - * @path: the path identifying the edited cell - * @new_text: the new text - * - * This signal is emitted after @renderer has been edited. - * - * It is the responsibility of the application to update the model - * and store @new_text at the position indicated by @path. - */ - text_cell_renderer_signals [EDITED] = - g_signal_new (I_("edited"), - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkCellRendererTextClass, edited), - NULL, NULL, - _gtk_marshal_VOID__STRING_STRING, - G_TYPE_NONE, 2, - G_TYPE_STRING, - G_TYPE_STRING); - g_signal_set_va_marshaller (text_cell_renderer_signals [EDITED], - G_OBJECT_CLASS_TYPE (object_class), - _gtk_marshal_VOID__STRING_STRINGv); -} - -static void -gtk_cell_renderer_text_finalize (GObject *object) -{ - GtkCellRendererText *celltext = GTK_CELL_RENDERER_TEXT (object); - GtkCellRendererTextPrivate *priv = gtk_cell_renderer_text_get_instance_private (celltext); - - pango_font_description_free (priv->font); - - g_free (priv->text); - g_free (priv->placeholder_text); - - if (priv->extra_attrs) - pango_attr_list_unref (priv->extra_attrs); - - if (priv->language) - g_object_unref (priv->language); - - g_clear_object (&priv->entry); - - G_OBJECT_CLASS (gtk_cell_renderer_text_parent_class)->finalize (object); -} - -static PangoFontMask -get_property_font_set_mask (guint prop_id) -{ - switch (prop_id) - { - case PROP_FAMILY_SET: - return PANGO_FONT_MASK_FAMILY; - case PROP_STYLE_SET: - return PANGO_FONT_MASK_STYLE; - case PROP_VARIANT_SET: - return PANGO_FONT_MASK_VARIANT; - case PROP_WEIGHT_SET: - return PANGO_FONT_MASK_WEIGHT; - case PROP_STRETCH_SET: - return PANGO_FONT_MASK_STRETCH; - case PROP_SIZE_SET: - return PANGO_FONT_MASK_SIZE; - default: - return 0; - } -} - -static void -gtk_cell_renderer_text_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec) -{ - GtkCellRendererText *celltext = GTK_CELL_RENDERER_TEXT (object); - GtkCellRendererTextPrivate *priv = gtk_cell_renderer_text_get_instance_private (celltext); - - switch (param_id) - { - case PROP_TEXT: - g_value_set_string (value, priv->text); - break; - - case PROP_ATTRIBUTES: - g_value_set_boxed (value, priv->extra_attrs); - break; - - case PROP_SINGLE_PARAGRAPH_MODE: - g_value_set_boolean (value, priv->single_paragraph); - break; - - case PROP_BACKGROUND_RGBA: - g_value_set_boxed (value, &priv->background); - break; - - case PROP_FOREGROUND_RGBA: - g_value_set_boxed (value, &priv->foreground); - break; - - case PROP_FONT: - g_value_take_string (value, pango_font_description_to_string (priv->font)); - break; - - case PROP_FONT_DESC: - g_value_set_boxed (value, priv->font); - break; - - case PROP_FAMILY: - g_value_set_string (value, pango_font_description_get_family (priv->font)); - break; - - case PROP_STYLE: - g_value_set_enum (value, pango_font_description_get_style (priv->font)); - break; - - case PROP_VARIANT: - g_value_set_enum (value, pango_font_description_get_variant (priv->font)); - break; - - case PROP_WEIGHT: - g_value_set_int (value, pango_font_description_get_weight (priv->font)); - break; - - case PROP_STRETCH: - g_value_set_enum (value, pango_font_description_get_stretch (priv->font)); - break; - - case PROP_SIZE: - g_value_set_int (value, pango_font_description_get_size (priv->font)); - break; - - case PROP_SIZE_POINTS: - g_value_set_double (value, ((double)pango_font_description_get_size (priv->font)) / (double)PANGO_SCALE); - break; - - case PROP_SCALE: - g_value_set_double (value, priv->font_scale); - break; - - case PROP_EDITABLE: - g_value_set_boolean (value, priv->editable); - break; - - case PROP_STRIKETHROUGH: - g_value_set_boolean (value, priv->strikethrough); - break; - - case PROP_UNDERLINE: - g_value_set_enum (value, priv->underline_style); - break; - - case PROP_RISE: - g_value_set_int (value, priv->rise); - break; - - case PROP_LANGUAGE: - g_value_set_static_string (value, pango_language_to_string (priv->language)); - break; - - case PROP_ELLIPSIZE: - g_value_set_enum (value, priv->ellipsize); - break; - - case PROP_WRAP_MODE: - g_value_set_enum (value, priv->wrap_mode); - break; - - case PROP_WRAP_WIDTH: - g_value_set_int (value, priv->wrap_width); - break; - - case PROP_ALIGN: - g_value_set_enum (value, priv->align); - break; - - case PROP_BACKGROUND_SET: - g_value_set_boolean (value, priv->background_set); - break; - - case PROP_FOREGROUND_SET: - g_value_set_boolean (value, priv->foreground_set); - break; - - case PROP_FAMILY_SET: - case PROP_STYLE_SET: - case PROP_VARIANT_SET: - case PROP_WEIGHT_SET: - case PROP_STRETCH_SET: - case PROP_SIZE_SET: - { - PangoFontMask mask = get_property_font_set_mask (param_id); - g_value_set_boolean (value, (pango_font_description_get_set_fields (priv->font) & mask) != 0); - - break; - } - - case PROP_SCALE_SET: - g_value_set_boolean (value, priv->scale_set); - break; - - case PROP_EDITABLE_SET: - g_value_set_boolean (value, priv->editable_set); - break; - - case PROP_STRIKETHROUGH_SET: - g_value_set_boolean (value, priv->strikethrough_set); - break; - - case PROP_UNDERLINE_SET: - g_value_set_boolean (value, priv->underline_set); - break; - - case PROP_RISE_SET: - g_value_set_boolean (value, priv->rise_set); - break; - - case PROP_LANGUAGE_SET: - g_value_set_boolean (value, priv->language_set); - break; - - case PROP_ELLIPSIZE_SET: - g_value_set_boolean (value, priv->ellipsize_set); - break; - - case PROP_ALIGN_SET: - g_value_set_boolean (value, priv->align_set); - break; - - case PROP_WIDTH_CHARS: - g_value_set_int (value, priv->width_chars); - break; - - case PROP_MAX_WIDTH_CHARS: - g_value_set_int (value, priv->max_width_chars); - break; - - case PROP_PLACEHOLDER_TEXT: - g_value_set_string (value, priv->placeholder_text); - break; - - case PROP_BACKGROUND: - case PROP_FOREGROUND: - case PROP_MARKUP: - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); - break; - } -} - - -static void -set_bg_color (GtkCellRendererText *celltext, - GdkRGBA *rgba) -{ - GtkCellRendererTextPrivate *priv = gtk_cell_renderer_text_get_instance_private (celltext); - - if (rgba) - { - if (!priv->background_set) - { - priv->background_set = TRUE; - g_object_notify_by_pspec (G_OBJECT (celltext), text_cell_renderer_props[PROP_BACKGROUND_SET]); - } - - priv->background = *rgba; - } - else - { - if (priv->background_set) - { - priv->background_set = FALSE; - g_object_notify_by_pspec (G_OBJECT (celltext), text_cell_renderer_props[PROP_BACKGROUND_SET]); - } - } -} - -static void -set_fg_color (GtkCellRendererText *celltext, - GdkRGBA *rgba) -{ - GtkCellRendererTextPrivate *priv = gtk_cell_renderer_text_get_instance_private (celltext); - - if (rgba) - { - if (!priv->foreground_set) - { - priv->foreground_set = TRUE; - g_object_notify_by_pspec (G_OBJECT (celltext), text_cell_renderer_props[PROP_FOREGROUND_SET]); - } - - priv->foreground = *rgba; - } - else - { - if (priv->foreground_set) - { - priv->foreground_set = FALSE; - g_object_notify_by_pspec (G_OBJECT (celltext), text_cell_renderer_props[PROP_FOREGROUND_SET]); - } - } -} - -static PangoFontMask -set_font_desc_fields (PangoFontDescription *desc, - PangoFontMask to_set) -{ - PangoFontMask changed_mask = 0; - - if (to_set & PANGO_FONT_MASK_FAMILY) - { - const char *family = pango_font_description_get_family (desc); - if (!family) - { - family = "sans"; - changed_mask |= PANGO_FONT_MASK_FAMILY; - } - - pango_font_description_set_family (desc, family); - } - if (to_set & PANGO_FONT_MASK_STYLE) - pango_font_description_set_style (desc, pango_font_description_get_style (desc)); - if (to_set & PANGO_FONT_MASK_VARIANT) - pango_font_description_set_variant (desc, pango_font_description_get_variant (desc)); - if (to_set & PANGO_FONT_MASK_WEIGHT) - pango_font_description_set_weight (desc, pango_font_description_get_weight (desc)); - if (to_set & PANGO_FONT_MASK_STRETCH) - pango_font_description_set_stretch (desc, pango_font_description_get_stretch (desc)); - if (to_set & PANGO_FONT_MASK_SIZE) - { - int size = pango_font_description_get_size (desc); - if (size <= 0) - { - size = 10 * PANGO_SCALE; - changed_mask |= PANGO_FONT_MASK_SIZE; - } - - pango_font_description_set_size (desc, size); - } - - return changed_mask; -} - -static void -notify_set_changed (GObject *object, - PangoFontMask changed_mask) -{ - if (changed_mask & PANGO_FONT_MASK_FAMILY) - g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_FAMILY_SET]); - if (changed_mask & PANGO_FONT_MASK_STYLE) - g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_STYLE_SET]); - if (changed_mask & PANGO_FONT_MASK_VARIANT) - g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_VARIANT_SET]); - if (changed_mask & PANGO_FONT_MASK_WEIGHT) - g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_WEIGHT_SET]); - if (changed_mask & PANGO_FONT_MASK_STRETCH) - g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_STRETCH_SET]); - if (changed_mask & PANGO_FONT_MASK_SIZE) - g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_SIZE_SET]); -} - -static void -notify_fields_changed (GObject *object, - PangoFontMask changed_mask) -{ - if (changed_mask & PANGO_FONT_MASK_FAMILY) - g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_FAMILY]); - if (changed_mask & PANGO_FONT_MASK_STYLE) - g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_STYLE]); - if (changed_mask & PANGO_FONT_MASK_VARIANT) - g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_VARIANT]); - if (changed_mask & PANGO_FONT_MASK_WEIGHT) - g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_WEIGHT]); - if (changed_mask & PANGO_FONT_MASK_STRETCH) - g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_STRETCH]); - if (changed_mask & PANGO_FONT_MASK_SIZE) - { - g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_SIZE]); - g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_SIZE_POINTS]); - } -} - -static void -set_font_description (GtkCellRendererText *celltext, - PangoFontDescription *font_desc) -{ - GtkCellRendererTextPrivate *priv = gtk_cell_renderer_text_get_instance_private (celltext); - GObject *object = G_OBJECT (celltext); - PangoFontDescription *new_font_desc; - PangoFontMask old_mask, new_mask, changed_mask, set_changed_mask; - - if (font_desc) - new_font_desc = pango_font_description_copy (font_desc); - else - new_font_desc = pango_font_description_new (); - - old_mask = pango_font_description_get_set_fields (priv->font); - new_mask = pango_font_description_get_set_fields (new_font_desc); - - changed_mask = old_mask | new_mask; - set_changed_mask = old_mask ^ new_mask; - - pango_font_description_free (priv->font); - priv->font = new_font_desc; - - g_object_freeze_notify (object); - - g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_FONT_DESC]); - g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_FONT]); - - notify_fields_changed (object, changed_mask); - notify_set_changed (object, set_changed_mask); - - g_object_thaw_notify (object); -} - -static void -gtk_cell_renderer_text_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec) -{ - GtkCellRendererText *celltext = GTK_CELL_RENDERER_TEXT (object); - GtkCellRendererTextPrivate *priv = gtk_cell_renderer_text_get_instance_private (celltext); - - switch (param_id) - { - case PROP_TEXT: - g_free (priv->text); - - if (priv->markup_set) - { - if (priv->extra_attrs) - pango_attr_list_unref (priv->extra_attrs); - priv->extra_attrs = NULL; - priv->markup_set = FALSE; - } - - priv->text = g_value_dup_string (value); - g_object_notify_by_pspec (object, pspec); - break; - - case PROP_ATTRIBUTES: - if (priv->extra_attrs) - pango_attr_list_unref (priv->extra_attrs); - - priv->extra_attrs = g_value_get_boxed (value); - if (priv->extra_attrs) - pango_attr_list_ref (priv->extra_attrs); - break; - case PROP_MARKUP: - { - const char *str; - char *text = NULL; - GError *error = NULL; - PangoAttrList *attrs = NULL; - - str = g_value_get_string (value); - if (str && !pango_parse_markup (str, -1, 0, &attrs, &text, NULL, &error)) - { - g_warning ("Failed to set text from markup due to error parsing markup: %s", - error->message); - g_error_free (error); - return; - } - - g_free (priv->text); - - if (priv->extra_attrs) - pango_attr_list_unref (priv->extra_attrs); - - priv->text = text; - priv->extra_attrs = attrs; - priv->markup_set = TRUE; - } - break; - - case PROP_SINGLE_PARAGRAPH_MODE: - if (priv->single_paragraph != g_value_get_boolean (value)) - { - priv->single_paragraph = g_value_get_boolean (value); - g_object_notify_by_pspec (object, pspec); - } - break; - - case PROP_BACKGROUND: - { - GdkRGBA rgba; - - if (!g_value_get_string (value)) - set_bg_color (celltext, NULL); /* reset to background_set to FALSE */ - else if (gdk_rgba_parse (&rgba, g_value_get_string (value))) - set_bg_color (celltext, &rgba); - else - g_warning ("Don't know color '%s'", g_value_get_string (value)); - } - break; - - case PROP_FOREGROUND: - { - GdkRGBA rgba; - - if (!g_value_get_string (value)) - set_fg_color (celltext, NULL); /* reset to foreground_set to FALSE */ - else if (gdk_rgba_parse (&rgba, g_value_get_string (value))) - set_fg_color (celltext, &rgba); - else - g_warning ("Don't know color '%s'", g_value_get_string (value)); - } - break; - - case PROP_BACKGROUND_RGBA: - set_bg_color (celltext, g_value_get_boxed (value)); - break; - - case PROP_FOREGROUND_RGBA: - set_fg_color (celltext, g_value_get_boxed (value)); - break; - - case PROP_FONT: - { - PangoFontDescription *font_desc = NULL; - const char *name; - - name = g_value_get_string (value); - - if (name) - font_desc = pango_font_description_from_string (name); - - set_font_description (celltext, font_desc); - - pango_font_description_free (font_desc); - - if (priv->fixed_height_rows != -1) - priv->calc_fixed_height = TRUE; - } - break; - - case PROP_FONT_DESC: - set_font_description (celltext, g_value_get_boxed (value)); - - if (priv->fixed_height_rows != -1) - priv->calc_fixed_height = TRUE; - break; - - case PROP_FAMILY: - case PROP_STYLE: - case PROP_VARIANT: - case PROP_WEIGHT: - case PROP_STRETCH: - case PROP_SIZE: - case PROP_SIZE_POINTS: - { - PangoFontMask old_set_mask = pango_font_description_get_set_fields (priv->font); - - switch (param_id) - { - case PROP_FAMILY: - pango_font_description_set_family (priv->font, - g_value_get_string (value)); - break; - case PROP_STYLE: - pango_font_description_set_style (priv->font, - g_value_get_enum (value)); - break; - case PROP_VARIANT: - pango_font_description_set_variant (priv->font, - g_value_get_enum (value)); - break; - case PROP_WEIGHT: - pango_font_description_set_weight (priv->font, - g_value_get_int (value)); - break; - case PROP_STRETCH: - pango_font_description_set_stretch (priv->font, - g_value_get_enum (value)); - break; - case PROP_SIZE: - pango_font_description_set_size (priv->font, - g_value_get_int (value)); - g_object_notify_by_pspec (object, pspec); - break; - case PROP_SIZE_POINTS: - pango_font_description_set_size (priv->font, - g_value_get_double (value) * PANGO_SCALE); - g_object_notify_by_pspec (object, pspec); - break; - default: - break; - } - - if (priv->fixed_height_rows != -1) - priv->calc_fixed_height = TRUE; - - notify_set_changed (object, old_set_mask & pango_font_description_get_set_fields (priv->font)); - g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_FONT_DESC]); - g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_FONT]); - - break; - } - - case PROP_SCALE: - priv->font_scale = g_value_get_double (value); - priv->scale_set = TRUE; - if (priv->fixed_height_rows != -1) - priv->calc_fixed_height = TRUE; - g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_SCALE_SET]); - break; - - case PROP_EDITABLE: - priv->editable = g_value_get_boolean (value); - priv->editable_set = TRUE; - if (priv->editable) - g_object_set (celltext, "mode", GTK_CELL_RENDERER_MODE_EDITABLE, NULL); - else - g_object_set (celltext, "mode", GTK_CELL_RENDERER_MODE_INERT, NULL); - g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_EDITABLE_SET]); - break; - - case PROP_STRIKETHROUGH: - priv->strikethrough = g_value_get_boolean (value); - priv->strikethrough_set = TRUE; - g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_STRIKETHROUGH_SET]); - break; - - case PROP_UNDERLINE: - priv->underline_style = g_value_get_enum (value); - priv->underline_set = TRUE; - g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_UNDERLINE_SET]); - - break; - - case PROP_RISE: - priv->rise = g_value_get_int (value); - priv->rise_set = TRUE; - g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_RISE_SET]); - if (priv->fixed_height_rows != -1) - priv->calc_fixed_height = TRUE; - break; - - case PROP_LANGUAGE: - priv->language_set = TRUE; - if (priv->language) - g_object_unref (priv->language); - priv->language = pango_language_from_string (g_value_get_string (value)); - g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_LANGUAGE_SET]); - break; - - case PROP_ELLIPSIZE: - priv->ellipsize = g_value_get_enum (value); - priv->ellipsize_set = TRUE; - g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_ELLIPSIZE_SET]); - break; - - case PROP_WRAP_MODE: - if (priv->wrap_mode != g_value_get_enum (value)) - { - priv->wrap_mode = g_value_get_enum (value); - g_object_notify_by_pspec (object, pspec); - } - break; - - case PROP_WRAP_WIDTH: - if (priv->wrap_width != g_value_get_int (value)) - { - priv->wrap_width = g_value_get_int (value); - g_object_notify_by_pspec (object, pspec); - } - break; - - case PROP_WIDTH_CHARS: - if (priv->width_chars != g_value_get_int (value)) - { - priv->width_chars = g_value_get_int (value); - g_object_notify_by_pspec (object, pspec); - } - break; - - case PROP_MAX_WIDTH_CHARS: - if (priv->max_width_chars != g_value_get_int (value)) - { - priv->max_width_chars = g_value_get_int (value); - g_object_notify_by_pspec (object, pspec); - } - break; - - case PROP_ALIGN: - if (priv->align != g_value_get_enum (value)) - { - priv->align = g_value_get_enum (value); - g_object_notify_by_pspec (object, pspec); - } - priv->align_set = TRUE; - g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_ALIGN_SET]); - break; - - case PROP_BACKGROUND_SET: - priv->background_set = g_value_get_boolean (value); - break; - - case PROP_FOREGROUND_SET: - priv->foreground_set = g_value_get_boolean (value); - break; - - case PROP_FAMILY_SET: - case PROP_STYLE_SET: - case PROP_VARIANT_SET: - case PROP_WEIGHT_SET: - case PROP_STRETCH_SET: - case PROP_SIZE_SET: - if (!g_value_get_boolean (value)) - { - pango_font_description_unset_fields (priv->font, - get_property_font_set_mask (param_id)); - } - else - { - PangoFontMask changed_mask; - - changed_mask = set_font_desc_fields (priv->font, - get_property_font_set_mask (param_id)); - notify_fields_changed (G_OBJECT (celltext), changed_mask); - } - break; - - case PROP_SCALE_SET: - priv->scale_set = g_value_get_boolean (value); - break; - - case PROP_EDITABLE_SET: - priv->editable_set = g_value_get_boolean (value); - break; - - case PROP_STRIKETHROUGH_SET: - priv->strikethrough_set = g_value_get_boolean (value); - break; - - case PROP_UNDERLINE_SET: - priv->underline_set = g_value_get_boolean (value); - break; - - case PROP_RISE_SET: - priv->rise_set = g_value_get_boolean (value); - break; - - case PROP_LANGUAGE_SET: - priv->language_set = g_value_get_boolean (value); - break; - - case PROP_ELLIPSIZE_SET: - priv->ellipsize_set = g_value_get_boolean (value); - break; - - case PROP_ALIGN_SET: - priv->align_set = g_value_get_boolean (value); - break; - - case PROP_PLACEHOLDER_TEXT: - g_free (priv->placeholder_text); - priv->placeholder_text = g_value_dup_string (value); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); - break; - } -} - -/** - * gtk_cell_renderer_text_new: - * - * Creates a new `GtkCellRendererText`. Adjust how text is drawn using - * object properties. Object properties can be - * set globally (with g_object_set()). Also, with `GtkTreeViewColumn`, - * you can bind a property to a value in a `GtkTreeModel`. For example, - * you can bind the “text” property on the cell renderer to a string - * value in the model, thus rendering a different string in each row - * of the `GtkTreeView`. - * - * Returns: the new cell renderer - **/ -GtkCellRenderer * -gtk_cell_renderer_text_new (void) -{ - return g_object_new (GTK_TYPE_CELL_RENDERER_TEXT, NULL); -} - -static inline gboolean -show_placeholder_text (GtkCellRendererText *celltext) -{ - GtkCellRendererTextPrivate *priv = gtk_cell_renderer_text_get_instance_private (celltext); - - return priv->editable && priv->placeholder_text && - (!priv->text || !priv->text[0]); -} - -static void -add_attr (PangoAttrList *attr_list, - PangoAttribute *attr) -{ - attr->start_index = 0; - attr->end_index = G_MAXINT; - - pango_attr_list_insert (attr_list, attr); -} - -static PangoLayout* -get_layout (GtkCellRendererText *celltext, - GtkWidget *widget, - const GdkRectangle *cell_area, - GtkCellRendererState flags) -{ - GtkCellRendererTextPrivate *priv = gtk_cell_renderer_text_get_instance_private (celltext); - PangoAttrList *attr_list; - PangoLayout *layout; - PangoUnderline uline; - int xpad; - gboolean placeholder_layout = show_placeholder_text (celltext); - - layout = gtk_widget_create_pango_layout (widget, placeholder_layout ? - priv->placeholder_text : priv->text); - - gtk_cell_renderer_get_padding (GTK_CELL_RENDERER (celltext), &xpad, NULL); - - if (priv->extra_attrs) - attr_list = pango_attr_list_copy (priv->extra_attrs); - else - attr_list = pango_attr_list_new (); - - pango_layout_set_single_paragraph_mode (layout, priv->single_paragraph); - - if (!placeholder_layout && cell_area) - { - /* Add options that affect appearance but not size */ - - /* note that background doesn't go here, since it affects - * background_area not the PangoLayout area - */ - - if (priv->foreground_set - && (flags & GTK_CELL_RENDERER_SELECTED) == 0) - { - PangoColor color; - guint16 alpha; - - color.red = CLAMP (priv->foreground.red * 65535. + 0.5, 0, 65535); - color.green = CLAMP (priv->foreground.green * 65535. + 0.5, 0, 65535); - color.blue = CLAMP (priv->foreground.blue * 65535. + 0.5, 0, 65535); - alpha = CLAMP (priv->foreground.alpha * 65535. + 0.5, 0, 65535); - - add_attr (attr_list, - pango_attr_foreground_new (color.red, color.green, color.blue)); - - add_attr (attr_list, pango_attr_foreground_alpha_new (alpha)); - } - - if (priv->strikethrough_set) - add_attr (attr_list, pango_attr_strikethrough_new (priv->strikethrough)); - } - else if (placeholder_layout) - { - PangoColor color; - guint16 alpha; - GtkStyleContext *context; - GdkRGBA fg = { 0.5, 0.5, 0.5, 1.0 }; - - context = gtk_widget_get_style_context (widget); - gtk_style_context_lookup_color (context, "placeholder_text_color", &fg); - - color.red = CLAMP (fg.red * 65535. + 0.5, 0, 65535); - color.green = CLAMP (fg.green * 65535. + 0.5, 0, 65535); - color.blue = CLAMP (fg.blue * 65535. + 0.5, 0, 65535); - alpha = CLAMP (fg.alpha * 65535. + 0.5, 0, 65535); - - add_attr (attr_list, - pango_attr_foreground_new (color.red, color.green, color.blue)); - - add_attr (attr_list, pango_attr_foreground_alpha_new (alpha)); - } - - add_attr (attr_list, pango_attr_font_desc_new (priv->font)); - - if (priv->scale_set && - priv->font_scale != 1.0) - add_attr (attr_list, pango_attr_scale_new (priv->font_scale)); - - if (priv->underline_set) - uline = priv->underline_style; - else - uline = PANGO_UNDERLINE_NONE; - - if (priv->language_set) - add_attr (attr_list, pango_attr_language_new (priv->language)); - - if ((flags & GTK_CELL_RENDERER_PRELIT) == GTK_CELL_RENDERER_PRELIT) - { - switch (uline) - { - case PANGO_UNDERLINE_NONE: - uline = PANGO_UNDERLINE_SINGLE; - break; - - case PANGO_UNDERLINE_SINGLE: - uline = PANGO_UNDERLINE_DOUBLE; - break; - - case PANGO_UNDERLINE_SINGLE_LINE: - uline = PANGO_UNDERLINE_DOUBLE_LINE; - break; - - case PANGO_UNDERLINE_DOUBLE_LINE: - case PANGO_UNDERLINE_ERROR_LINE: - break; - - case PANGO_UNDERLINE_DOUBLE: - case PANGO_UNDERLINE_LOW: - case PANGO_UNDERLINE_ERROR: - default: - break; - } - } - - if (uline != PANGO_UNDERLINE_NONE) - add_attr (attr_list, pango_attr_underline_new (priv->underline_style)); - - if (priv->rise_set) - add_attr (attr_list, pango_attr_rise_new (priv->rise)); - - /* Now apply the attributes as they will effect the outcome - * of pango_layout_get_extents() */ - pango_layout_set_attributes (layout, attr_list); - pango_attr_list_unref (attr_list); - - if (priv->ellipsize_set) - pango_layout_set_ellipsize (layout, priv->ellipsize); - else - pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_NONE); - - if (priv->wrap_width != -1) - { - PangoRectangle rect; - int width, text_width; - - pango_layout_get_extents (layout, NULL, &rect); - text_width = rect.width; - - if (cell_area) - width = (cell_area->width - xpad * 2) * PANGO_SCALE; - else - width = priv->wrap_width * PANGO_SCALE; - - width = MIN (width, text_width); - - pango_layout_set_width (layout, width); - pango_layout_set_wrap (layout, priv->wrap_mode); - } - else - { - pango_layout_set_width (layout, -1); - pango_layout_set_wrap (layout, PANGO_WRAP_CHAR); - } - - if (priv->align_set) - pango_layout_set_alignment (layout, priv->align); - else - { - PangoAlignment align; - - if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) - align = PANGO_ALIGN_RIGHT; - else - align = PANGO_ALIGN_LEFT; - - pango_layout_set_alignment (layout, align); - } - - return layout; -} - - -static void -get_size (GtkCellRenderer *cell, - GtkWidget *widget, - const GdkRectangle *cell_area, - PangoLayout *layout, - int *x_offset, - int *y_offset, - int *width, - int *height) -{ - GtkCellRendererText *celltext = GTK_CELL_RENDERER_TEXT (cell); - GtkCellRendererTextPrivate *priv = gtk_cell_renderer_text_get_instance_private (celltext); - PangoRectangle rect; - int xpad, ypad; - int cell_width, cell_height; - float xalign, yalign; - - gtk_cell_renderer_get_padding (cell, &xpad, &ypad); - - if (priv->calc_fixed_height) - { - GtkStyleContext *style_context; - PangoContext *context; - PangoFontMetrics *metrics; - PangoFontDescription *font_desc; - int row_height; - - style_context = gtk_widget_get_style_context (widget); - - font_desc = gtk_css_style_get_pango_font (gtk_style_context_lookup_style (style_context)); - pango_font_description_merge_static (font_desc, priv->font, TRUE); - - if (priv->scale_set) - pango_font_description_set_size (font_desc, - priv->font_scale * pango_font_description_get_size (font_desc)); - - context = gtk_widget_get_pango_context (widget); - - metrics = pango_context_get_metrics (context, - font_desc, - pango_context_get_language (context)); - row_height = (pango_font_metrics_get_ascent (metrics) + - pango_font_metrics_get_descent (metrics)); - pango_font_metrics_unref (metrics); - - pango_font_description_free (font_desc); - - gtk_cell_renderer_get_fixed_size (cell, &cell_width, &cell_height); - - gtk_cell_renderer_set_fixed_size (cell, - cell_width, 2 * ypad + - priv->fixed_height_rows * PANGO_PIXELS (row_height)); - - if (height) - { - *height = cell_height; - height = NULL; - } - priv->calc_fixed_height = FALSE; - if (width == NULL) - return; - } - - pango_layout_get_pixel_extents (layout, NULL, &rect); - - gtk_cell_renderer_get_alignment (cell, &xalign, &yalign); - - rect.height = MIN (rect.height, cell_area->height - 2 * ypad); - rect.width = MIN (rect.width, cell_area->width - 2 * xpad); - - if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) - *x_offset = (1.0 - xalign) * (cell_area->width - (rect.width + (2 * xpad))); - else - *x_offset = xalign * (cell_area->width - (rect.width + (2 * xpad))); - - if ((priv->ellipsize_set && priv->ellipsize != PANGO_ELLIPSIZE_NONE) || priv->wrap_width != -1) - *x_offset = MAX(*x_offset, 0); - - *y_offset = yalign * (cell_area->height - (rect.height + (2 * ypad))); - *y_offset = MAX (*y_offset, 0); - - if (height) - *height = ypad * 2 + rect.height; - - if (width) - *width = xpad * 2 + rect.width; -} - -static void -gtk_cell_renderer_text_snapshot (GtkCellRenderer *cell, - GtkSnapshot *snapshot, - GtkWidget *widget, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - GtkCellRendererState flags) - -{ - GtkCellRendererText *celltext = GTK_CELL_RENDERER_TEXT (cell); - GtkCellRendererTextPrivate *priv = gtk_cell_renderer_text_get_instance_private (celltext); - GtkStyleContext *context; - PangoLayout *layout; - int x_offset = 0; - int y_offset = 0; - int xpad, ypad; - PangoRectangle rect; - - layout = get_layout (celltext, widget, cell_area, flags); - get_size (cell, widget, cell_area, layout, &x_offset, &y_offset, NULL, NULL); - context = gtk_widget_get_style_context (widget); - - if (priv->background_set && (flags & GTK_CELL_RENDERER_SELECTED) == 0) - { - gtk_snapshot_append_color (snapshot, - &priv->background, - &GRAPHENE_RECT_INIT( - background_area->x, background_area->y, - background_area->width, background_area->height - )); - } - - gtk_cell_renderer_get_padding (cell, &xpad, &ypad); - - if (priv->ellipsize_set && priv->ellipsize != PANGO_ELLIPSIZE_NONE) - pango_layout_set_width (layout, - (cell_area->width - x_offset - 2 * xpad) * PANGO_SCALE); - else if (priv->wrap_width == -1) - pango_layout_set_width (layout, -1); - - pango_layout_get_pixel_extents (layout, NULL, &rect); - x_offset = x_offset - rect.x; - - gtk_snapshot_push_clip (snapshot, - &GRAPHENE_RECT_INIT( - cell_area->x, cell_area->y, - cell_area->width, cell_area->height - )); - - gtk_snapshot_render_layout (snapshot, context, - cell_area->x + x_offset + xpad, - cell_area->y + y_offset + ypad, - layout); - - gtk_snapshot_pop (snapshot); - - g_object_unref (layout); -} - -static void -gtk_cell_renderer_text_editing_done (GtkCellEditable *entry, - gpointer data) -{ - GtkCellRendererText *celltext = GTK_CELL_RENDERER_TEXT (data); - GtkCellRendererTextPrivate *priv = gtk_cell_renderer_text_get_instance_private (celltext); - const char *path; - const char *new_text; - gboolean canceled; - - g_clear_object (&priv->entry); - - if (priv->focus_out_id > 0) - { - g_signal_handler_disconnect (entry, priv->focus_out_id); - priv->focus_out_id = 0; - } - - if (priv->entry_menu_popdown_timeout) - { - g_source_remove (priv->entry_menu_popdown_timeout); - priv->entry_menu_popdown_timeout = 0; - } - - g_object_get (entry, - "editing-canceled", &canceled, - NULL); - gtk_cell_renderer_stop_editing (GTK_CELL_RENDERER (data), canceled); - - if (canceled) - return; - - path = g_object_get_data (G_OBJECT (entry), GTK_CELL_RENDERER_TEXT_PATH); - new_text = gtk_editable_get_text (GTK_EDITABLE (entry)); - g_signal_emit (data, text_cell_renderer_signals[EDITED], 0, path, new_text); -} - -static void -gtk_cell_renderer_text_focus_changed (GtkWidget *entry, - GParamSpec *pspec, - gpointer data) -{ - if (gtk_widget_has_focus (entry) || - gtk_widget_has_focus (GTK_WIDGET (gtk_entry_get_text_widget (GTK_ENTRY (entry))))) - return; - - g_object_set (entry, - "editing-canceled", TRUE, - NULL); - gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (entry)); - gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (entry)); -} - -static GtkCellEditable * -gtk_cell_renderer_text_start_editing (GtkCellRenderer *cell, - GdkEvent *event, - GtkWidget *widget, - const char *path, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - GtkCellRendererState flags) -{ - GtkCellRendererText *celltext = GTK_CELL_RENDERER_TEXT (cell); - GtkCellRendererTextPrivate *priv = gtk_cell_renderer_text_get_instance_private (celltext); - float xalign, yalign; - - /* If the cell isn't editable we return NULL. */ - if (!priv->editable) - return NULL; - - gtk_cell_renderer_get_alignment (cell, &xalign, &yalign); - - priv->entry = gtk_entry_new (); - g_object_ref_sink (G_OBJECT (priv->entry)); - - gtk_entry_set_has_frame (GTK_ENTRY (priv->entry), FALSE); - gtk_entry_set_alignment (GTK_ENTRY (priv->entry), xalign); - gtk_editable_set_width_chars (GTK_EDITABLE (priv->entry), 5); - - if (priv->text) - gtk_editable_set_text (GTK_EDITABLE (priv->entry), priv->text); - g_object_set_data_full (G_OBJECT (priv->entry), I_(GTK_CELL_RENDERER_TEXT_PATH), g_strdup (path), g_free); - - gtk_editable_select_region (GTK_EDITABLE (priv->entry), 0, -1); - - priv->in_entry_menu = FALSE; - if (priv->entry_menu_popdown_timeout) - { - g_source_remove (priv->entry_menu_popdown_timeout); - priv->entry_menu_popdown_timeout = 0; - } - - g_signal_connect (priv->entry, "editing-done", - G_CALLBACK (gtk_cell_renderer_text_editing_done), celltext); - priv->focus_out_id = g_signal_connect_after (priv->entry, "notify::has-focus", - G_CALLBACK (gtk_cell_renderer_text_focus_changed), - celltext); - - return GTK_CELL_EDITABLE (priv->entry); -} - -/** - * gtk_cell_renderer_text_set_fixed_height_from_font: - * @renderer: A `GtkCellRendererText` - * @number_of_rows: Number of rows of text each cell renderer is allocated, or -1 - * - * Sets the height of a renderer to explicitly be determined by the “font” and - * “y_pad” property set on it. Further changes in these properties do not - * affect the height, so they must be accompanied by a subsequent call to this - * function. Using this function is inflexible, and should really only be used - * if calculating the size of a cell is too slow (ie, a massive number of cells - * displayed). If @number_of_rows is -1, then the fixed height is unset, and - * the height is determined by the properties again. - **/ -void -gtk_cell_renderer_text_set_fixed_height_from_font (GtkCellRendererText *renderer, - int number_of_rows) -{ - GtkCellRendererTextPrivate *priv = gtk_cell_renderer_text_get_instance_private (renderer); - GtkCellRenderer *cell = GTK_CELL_RENDERER (renderer); - - g_return_if_fail (GTK_IS_CELL_RENDERER_TEXT (renderer)); - g_return_if_fail (number_of_rows == -1 || number_of_rows > 0); - - if (number_of_rows == -1) - { - int width, height; - - gtk_cell_renderer_get_fixed_size (cell, &width, &height); - gtk_cell_renderer_set_fixed_size (cell, width, -1); - } - else - { - priv->fixed_height_rows = number_of_rows; - priv->calc_fixed_height = TRUE; - } -} - -static void -gtk_cell_renderer_text_get_preferred_width (GtkCellRenderer *cell, - GtkWidget *widget, - int *minimum_size, - int *natural_size) -{ - GtkCellRendererText *celltext = GTK_CELL_RENDERER_TEXT (cell); - GtkCellRendererTextPrivate *priv = gtk_cell_renderer_text_get_instance_private (celltext); - PangoLayout *layout; - PangoContext *context; - PangoFontMetrics *metrics; - PangoRectangle rect; - int char_width, text_width, ellipsize_chars, xpad; - int min_width, nat_width; - - /* "width-chars" Hard-coded minimum width: - * - minimum size should be MAX (width-chars, strlen ("...")); - * - natural size should be MAX (width-chars, strlen (label->text)); - * - * "wrap-width" User specified natural wrap width - * - minimum size should be MAX (width-chars, 0) - * - natural size should be MIN (wrap-width, strlen (label->text)) - */ - gtk_cell_renderer_get_padding (cell, &xpad, NULL); - - layout = get_layout (celltext, widget, NULL, 0); - - /* Fetch the length of the complete unwrapped text */ - pango_layout_set_width (layout, -1); - pango_layout_get_extents (layout, NULL, &rect); - text_width = rect.width; - - /* Fetch the average size of a character */ - context = pango_layout_get_context (layout); - metrics = pango_context_get_metrics (context, - pango_context_get_font_description (context), - pango_context_get_language (context)); - - char_width = pango_font_metrics_get_approximate_char_width (metrics); - - pango_font_metrics_unref (metrics); - g_object_unref (layout); - - /* enforce minimum width for ellipsized labels at ~3 chars */ - if (priv->ellipsize_set && priv->ellipsize != PANGO_ELLIPSIZE_NONE) - ellipsize_chars = 3; - else - ellipsize_chars = 0; - - if ((priv->ellipsize_set && priv->ellipsize != PANGO_ELLIPSIZE_NONE) || priv->width_chars > 0) - min_width = xpad * 2 + - MIN (PANGO_PIXELS_CEIL (text_width), - (PANGO_PIXELS (char_width) * MAX (priv->width_chars, ellipsize_chars))); - /* If no width-chars set, minimum for wrapping text will be the wrap-width */ - else if (priv->wrap_width > -1) - min_width = xpad * 2 + rect.x + MIN (PANGO_PIXELS_CEIL (text_width), priv->wrap_width); - else - min_width = xpad * 2 + rect.x + PANGO_PIXELS_CEIL (text_width); - - if (priv->width_chars > 0) - nat_width = xpad * 2 + - MAX ((PANGO_PIXELS (char_width) * priv->width_chars), PANGO_PIXELS_CEIL (text_width)); - else - nat_width = xpad * 2 + PANGO_PIXELS_CEIL (text_width); - - nat_width = MAX (nat_width, min_width); - - if (priv->max_width_chars > 0) - { - int max_width = xpad * 2 + PANGO_PIXELS (char_width) * priv->max_width_chars; - - min_width = MIN (min_width, max_width); - nat_width = MIN (nat_width, max_width); - } - - if (minimum_size) - *minimum_size = min_width; - - if (natural_size) - *natural_size = nat_width; -} - -static void -gtk_cell_renderer_text_get_preferred_height_for_width (GtkCellRenderer *cell, - GtkWidget *widget, - int width, - int *minimum_height, - int *natural_height) -{ - GtkCellRendererText *celltext = GTK_CELL_RENDERER_TEXT (cell); - PangoLayout *layout; - int text_height, xpad, ypad; - - gtk_cell_renderer_get_padding (cell, &xpad, &ypad); - - layout = get_layout (celltext, widget, NULL, 0); - - pango_layout_set_width (layout, (width - xpad * 2) * PANGO_SCALE); - pango_layout_get_pixel_size (layout, NULL, &text_height); - - if (minimum_height) - *minimum_height = text_height + ypad * 2; - - if (natural_height) - *natural_height = text_height + ypad * 2; - - g_object_unref (layout); -} - -static void -gtk_cell_renderer_text_get_preferred_height (GtkCellRenderer *cell, - GtkWidget *widget, - int *minimum_size, - int *natural_size) -{ - int min_width; - - /* Thankfully cell renderers dont rotate, so they only have to do - * height-for-width and not the opposite. Here we have only to return - * the height for the base minimum width of the renderer. - * - * Note this code path won't be followed by GtkTreeView which is - * height-for-width specifically. - */ - gtk_cell_renderer_get_preferred_width (cell, widget, &min_width, NULL); - gtk_cell_renderer_text_get_preferred_height_for_width (cell, widget, min_width, - minimum_size, natural_size); -} - -static void -gtk_cell_renderer_text_get_aligned_area (GtkCellRenderer *cell, - GtkWidget *widget, - GtkCellRendererState flags, - const GdkRectangle *cell_area, - GdkRectangle *aligned_area) -{ - GtkCellRendererText *celltext = GTK_CELL_RENDERER_TEXT (cell); - PangoLayout *layout; - int x_offset = 0; - int y_offset = 0; - - layout = get_layout (celltext, widget, cell_area, flags); - get_size (cell, widget, cell_area, layout, &x_offset, &y_offset, - &aligned_area->width, &aligned_area->height); - - aligned_area->x = cell_area->x + x_offset; - aligned_area->y = cell_area->y + y_offset; - - g_object_unref (layout); -} diff --git a/gtk/gtkcellrenderertext.h b/gtk/gtkcellrenderertext.h deleted file mode 100644 index e02a31ee74..0000000000 --- a/gtk/gtkcellrenderertext.h +++ /dev/null @@ -1,73 +0,0 @@ -/* gtkcellrenderertext.h - * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#ifndef __GTK_CELL_RENDERER_TEXT_H__ -#define __GTK_CELL_RENDERER_TEXT_H__ - - -#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) -#error "Only can be included directly." -#endif - -#include - - -G_BEGIN_DECLS - - -#define GTK_TYPE_CELL_RENDERER_TEXT (gtk_cell_renderer_text_get_type ()) -#define GTK_CELL_RENDERER_TEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_RENDERER_TEXT, GtkCellRendererText)) -#define GTK_CELL_RENDERER_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_CELL_RENDERER_TEXT, GtkCellRendererTextClass)) -#define GTK_IS_CELL_RENDERER_TEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_RENDERER_TEXT)) -#define GTK_IS_CELL_RENDERER_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_CELL_RENDERER_TEXT)) -#define GTK_CELL_RENDERER_TEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CELL_RENDERER_TEXT, GtkCellRendererTextClass)) - -typedef struct _GtkCellRendererText GtkCellRendererText; -typedef struct _GtkCellRendererTextClass GtkCellRendererTextClass; - -struct _GtkCellRendererText -{ - GtkCellRenderer parent; -}; - -struct _GtkCellRendererTextClass -{ - GtkCellRendererClass parent_class; - - void (* edited) (GtkCellRendererText *cell_renderer_text, - const char *path, - const char *new_text); - - /*< private >*/ - - gpointer padding[8]; -}; - -GDK_AVAILABLE_IN_ALL -GType gtk_cell_renderer_text_get_type (void) G_GNUC_CONST; -GDK_AVAILABLE_IN_ALL -GtkCellRenderer *gtk_cell_renderer_text_new (void); - -GDK_AVAILABLE_IN_ALL -void gtk_cell_renderer_text_set_fixed_height_from_font (GtkCellRendererText *renderer, - int number_of_rows); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkCellRendererText, g_object_unref) - -G_END_DECLS - -#endif /* __GTK_CELL_RENDERER_TEXT_H__ */ diff --git a/gtk/gtkcellrenderertoggle.c b/gtk/gtkcellrenderertoggle.c deleted file mode 100644 index 02778c5bf6..0000000000 --- a/gtk/gtkcellrenderertoggle.c +++ /dev/null @@ -1,639 +0,0 @@ -/* gtkcellrenderertoggle.c - * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#include "config.h" - -#include "gtkcellrenderertoggle.h" - -#include "gtkcssnumbervalueprivate.h" -#include "gtkcsstransientnodeprivate.h" -#include "gtkmarshalers.h" -#include "gtkprivate.h" -#include "gtkrendericonprivate.h" -#include "gtksnapshot.h" -#include "gtkstylecontextprivate.h" -#include "gtkwidgetprivate.h" -#include "gtktreeprivate.h" - -#include - -/** - * GtkCellRendererToggle: - * - * Renders a toggle button in a cell - * - * `GtkCellRendererToggle` renders a toggle button in a cell. The - * button is drawn as a radio or a checkbutton, depending on the - * `GtkCellRendererToggle:radio` property. - * When activated, it emits the `GtkCellRendererToggle::toggled` signal. - */ - - -static void gtk_cell_renderer_toggle_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec); -static void gtk_cell_renderer_toggle_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec); -static void gtk_cell_renderer_toggle_get_size (GtkCellRendererToggle *self, - GtkWidget *widget, - const GdkRectangle *cell_area, - int *x_offset, - int *y_offset, - int *width, - int *height); -static void gtk_cell_renderer_toggle_snapshot (GtkCellRenderer *cell, - GtkSnapshot *snapshot, - GtkWidget *widget, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - GtkCellRendererState flags); -static gboolean gtk_cell_renderer_toggle_activate (GtkCellRenderer *cell, - GdkEvent *event, - GtkWidget *widget, - const char *path, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - GtkCellRendererState flags); - - -enum { - TOGGLED, - LAST_SIGNAL -}; - -enum { - PROP_0, - PROP_ACTIVATABLE, - PROP_ACTIVE, - PROP_RADIO, - PROP_INCONSISTENT -}; - -static guint toggle_cell_signals[LAST_SIGNAL] = { 0 }; - -typedef struct _GtkCellRendererTogglePrivate GtkCellRendererTogglePrivate; -typedef struct _GtkCellRendererToggleClass GtkCellRendererToggleClass; - -struct _GtkCellRendererToggle -{ - GtkCellRenderer parent; -}; - -struct _GtkCellRendererToggleClass -{ - GtkCellRendererClass parent_class; - - void (* toggled) (GtkCellRendererToggle *cell, - const char *path); -}; - -struct _GtkCellRendererTogglePrivate -{ - guint active : 1; - guint activatable : 1; - guint inconsistent : 1; - guint radio : 1; - GtkCssNode *cssnode; -}; - - -G_DEFINE_TYPE_WITH_PRIVATE (GtkCellRendererToggle, gtk_cell_renderer_toggle, GTK_TYPE_CELL_RENDERER) - - -static void -gtk_cell_renderer_toggle_init (GtkCellRendererToggle *celltoggle) -{ - GtkCellRendererTogglePrivate *priv = gtk_cell_renderer_toggle_get_instance_private (celltoggle); - - priv->activatable = TRUE; - priv->active = FALSE; - priv->radio = FALSE; - - g_object_set (celltoggle, "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL); - gtk_cell_renderer_set_padding (GTK_CELL_RENDERER (celltoggle), 2, 2); - - priv->inconsistent = FALSE; -} - -static GtkSizeRequestMode -gtk_cell_renderer_toggle_get_request_mode (GtkCellRenderer *cell) -{ - return GTK_SIZE_REQUEST_CONSTANT_SIZE; -} - -static void -gtk_cell_renderer_toggle_get_preferred_width (GtkCellRenderer *cell, - GtkWidget *widget, - int *minimum, - int *natural) -{ - int width = 0; - - gtk_cell_renderer_toggle_get_size (GTK_CELL_RENDERER_TOGGLE (cell), widget, - NULL, - NULL, NULL, &width, NULL); - - if (minimum) - *minimum = width; - if (natural) - *natural = width; -} - -static void -gtk_cell_renderer_toggle_get_preferred_height (GtkCellRenderer *cell, - GtkWidget *widget, - int *minimum, - int *natural) -{ - int height = 0; - - gtk_cell_renderer_toggle_get_size (GTK_CELL_RENDERER_TOGGLE (cell), widget, - NULL, - NULL, NULL, NULL, &height); - - if (minimum) - *minimum = height; - if (natural) - *natural = height; -} - -static void -gtk_cell_renderer_toggle_class_init (GtkCellRendererToggleClass *class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (class); - GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (class); - - object_class->get_property = gtk_cell_renderer_toggle_get_property; - object_class->set_property = gtk_cell_renderer_toggle_set_property; - - cell_class->get_request_mode = gtk_cell_renderer_toggle_get_request_mode; - cell_class->get_preferred_width = gtk_cell_renderer_toggle_get_preferred_width; - cell_class->get_preferred_height = gtk_cell_renderer_toggle_get_preferred_height; - cell_class->snapshot = gtk_cell_renderer_toggle_snapshot; - cell_class->activate = gtk_cell_renderer_toggle_activate; - - g_object_class_install_property (object_class, - PROP_ACTIVE, - g_param_spec_boolean ("active", NULL, NULL, - FALSE, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - g_object_class_install_property (object_class, - PROP_INCONSISTENT, - g_param_spec_boolean ("inconsistent", NULL, NULL, - FALSE, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - g_object_class_install_property (object_class, - PROP_ACTIVATABLE, - g_param_spec_boolean ("activatable", NULL, NULL, - TRUE, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - g_object_class_install_property (object_class, - PROP_RADIO, - g_param_spec_boolean ("radio", NULL, NULL, - FALSE, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - - /** - * GtkCellRendererToggle::toggled: - * @cell_renderer: the object which received the signal - * @path: string representation of `GtkTreePath` describing the - * event location - * - * The ::toggled signal is emitted when the cell is toggled. - * - * It is the responsibility of the application to update the model - * with the correct value to store at @path. Often this is simply the - * opposite of the value currently stored at @path. - **/ - toggle_cell_signals[TOGGLED] = - g_signal_new (I_("toggled"), - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkCellRendererToggleClass, toggled), - NULL, NULL, - NULL, - G_TYPE_NONE, 1, - G_TYPE_STRING); -} - -static void -gtk_cell_renderer_toggle_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec) -{ - GtkCellRendererToggle *celltoggle = GTK_CELL_RENDERER_TOGGLE (object); - GtkCellRendererTogglePrivate *priv = gtk_cell_renderer_toggle_get_instance_private (celltoggle); - - switch (param_id) - { - case PROP_ACTIVE: - g_value_set_boolean (value, priv->active); - break; - case PROP_INCONSISTENT: - g_value_set_boolean (value, priv->inconsistent); - break; - case PROP_ACTIVATABLE: - g_value_set_boolean (value, priv->activatable); - break; - case PROP_RADIO: - g_value_set_boolean (value, priv->radio); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); - break; - } -} - - -static void -gtk_cell_renderer_toggle_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec) -{ - GtkCellRendererToggle *celltoggle = GTK_CELL_RENDERER_TOGGLE (object); - GtkCellRendererTogglePrivate *priv = gtk_cell_renderer_toggle_get_instance_private (celltoggle); - - switch (param_id) - { - case PROP_ACTIVE: - if (priv->active != g_value_get_boolean (value)) - { - priv->active = g_value_get_boolean (value); - g_object_notify_by_pspec (object, pspec); - } - break; - case PROP_INCONSISTENT: - if (priv->inconsistent != g_value_get_boolean (value)) - { - priv->inconsistent = g_value_get_boolean (value); - g_object_notify_by_pspec (object, pspec); - } - break; - case PROP_ACTIVATABLE: - if (priv->activatable != g_value_get_boolean (value)) - { - priv->activatable = g_value_get_boolean (value); - g_object_notify_by_pspec (object, pspec); - } - break; - case PROP_RADIO: - if (priv->radio != g_value_get_boolean (value)) - { - gtk_cell_renderer_toggle_set_radio (celltoggle, g_value_get_boolean (value)); - g_object_notify_by_pspec (object, pspec); - } - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); - break; - } -} - -/** - * gtk_cell_renderer_toggle_new: - * - * Creates a new `GtkCellRendererToggle`. Adjust rendering - * parameters using object properties. Object properties can be set - * globally (with g_object_set()). Also, with `GtkTreeViewColumn`, you - * can bind a property to a value in a `GtkTreeModel`. For example, you - * can bind the “active” property on the cell renderer to a boolean value - * in the model, thus causing the check button to reflect the state of - * the model. - * - * Returns: the new cell renderer - **/ -GtkCellRenderer * -gtk_cell_renderer_toggle_new (void) -{ - return g_object_new (GTK_TYPE_CELL_RENDERER_TOGGLE, NULL); -} - -static GtkStyleContext * -gtk_cell_renderer_toggle_save_context (GtkCellRendererToggle *cell, - GtkWidget *widget) -{ - GtkCellRendererTogglePrivate *priv = gtk_cell_renderer_toggle_get_instance_private (cell); - GtkStyleContext *context; - GtkCssNode *cssnode; - - context = gtk_widget_get_style_context (widget); - - cssnode = gtk_css_transient_node_new (gtk_widget_get_css_node (widget)); - if (priv->radio) - gtk_css_node_set_name (cssnode, g_quark_from_static_string ("radio")); - else - gtk_css_node_set_name (cssnode, g_quark_from_static_string ("check")); - gtk_style_context_save_to_node (context, cssnode); - g_object_unref (cssnode); - - return context; -} - -static void -gtk_cell_renderer_toggle_restore_context (GtkCellRendererToggle *cell, - GtkStyleContext *context) -{ - gtk_style_context_restore (context); -} - -static int -calc_indicator_size (GtkStyleContext *context) -{ - GtkCssStyle *style = gtk_style_context_lookup_style (context); - return _gtk_css_number_value_get (style->icon->icon_size, 100); -} - -static void -gtk_cell_renderer_toggle_get_size (GtkCellRendererToggle *self, - GtkWidget *widget, - const GdkRectangle *cell_area, - int *x_offset, - int *y_offset, - int *width, - int *height) -{ - GtkCellRenderer *cell = GTK_CELL_RENDERER (self); - int calc_width; - int calc_height; - int xpad, ypad; - GtkStyleContext *context; - GtkBorder border, padding; - - gtk_cell_renderer_get_padding (cell, &xpad, &ypad); - - context = gtk_cell_renderer_toggle_save_context (self, widget); - gtk_style_context_get_padding (context, &padding); - gtk_style_context_get_border (context, &border); - - calc_width = calc_height = calc_indicator_size (context); - calc_width += xpad * 2 + padding.left + padding.right + border.left + border.right; - calc_height += ypad * 2 + padding.top + padding.bottom + border.top + border.bottom; - - gtk_cell_renderer_toggle_restore_context (self, context); - - if (width) - *width = calc_width; - - if (height) - *height = calc_height; - - if (cell_area) - { - float xalign, yalign; - - gtk_cell_renderer_get_alignment (cell, &xalign, &yalign); - - if (x_offset) - { - *x_offset = ((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) ? - (1.0 - xalign) : xalign) * (cell_area->width - calc_width); - *x_offset = MAX (*x_offset, 0); - } - if (y_offset) - { - *y_offset = yalign * (cell_area->height - calc_height); - *y_offset = MAX (*y_offset, 0); - } - } - else - { - if (x_offset) - *x_offset = 0; - if (y_offset) - *y_offset = 0; - } -} - -static void -gtk_cell_renderer_toggle_snapshot (GtkCellRenderer *cell, - GtkSnapshot *snapshot, - GtkWidget *widget, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - GtkCellRendererState flags) -{ - GtkCellRendererToggle *celltoggle = GTK_CELL_RENDERER_TOGGLE (cell); - GtkCellRendererTogglePrivate *priv = gtk_cell_renderer_toggle_get_instance_private (celltoggle); - GtkStyleContext *context; - int width, height; - int x_offset, y_offset; - int xpad, ypad; - GtkStateFlags state; - GtkBorder padding, border; - - gtk_cell_renderer_toggle_get_size (celltoggle, widget, cell_area, - &x_offset, &y_offset, - &width, &height); - gtk_cell_renderer_get_padding (cell, &xpad, &ypad); - width -= xpad * 2; - height -= ypad * 2; - - if (width <= 0 || height <= 0) - return; - - state = gtk_cell_renderer_get_state (cell, widget, flags); - - if (!priv->activatable) - state |= GTK_STATE_FLAG_INSENSITIVE; - - state &= ~(GTK_STATE_FLAG_INCONSISTENT | GTK_STATE_FLAG_CHECKED); - - if (priv->inconsistent) - state |= GTK_STATE_FLAG_INCONSISTENT; - - if (priv->active) - state |= GTK_STATE_FLAG_CHECKED; - - gtk_snapshot_push_clip (snapshot, - &GRAPHENE_RECT_INIT ( - cell_area->x, cell_area->y, - cell_area->width, cell_area->height - )); - - context = gtk_cell_renderer_toggle_save_context (celltoggle, widget); - gtk_style_context_set_state (context, state); - - gtk_snapshot_render_background (snapshot, context, - cell_area->x + x_offset + xpad, - cell_area->y + y_offset + ypad, - width, height); - gtk_snapshot_render_frame (snapshot, context, - cell_area->x + x_offset + xpad, - cell_area->y + y_offset + ypad, - width, height); - - gtk_style_context_get_padding (context, &padding); - gtk_style_context_get_border (context, &border); - - gtk_snapshot_translate (snapshot, - &GRAPHENE_POINT_INIT (cell_area->x + x_offset + xpad + padding.left + border.left, - cell_area->y + y_offset + ypad + padding.top + border.top)); - gtk_css_style_snapshot_icon (gtk_style_context_lookup_style (context), snapshot, - width - padding.left - padding.right - border.left - border.right, - height - padding.top - padding.bottom - border.top - border.bottom); - - gtk_cell_renderer_toggle_restore_context (celltoggle, context); - gtk_snapshot_pop (snapshot); -} - -static int -gtk_cell_renderer_toggle_activate (GtkCellRenderer *cell, - GdkEvent *event, - GtkWidget *widget, - const char *path, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - GtkCellRendererState flags) -{ - GtkCellRendererToggle *celltoggle = GTK_CELL_RENDERER_TOGGLE (cell); - GtkCellRendererTogglePrivate *priv = gtk_cell_renderer_toggle_get_instance_private (celltoggle); - - if (priv->activatable) - { - g_signal_emit (cell, toggle_cell_signals[TOGGLED], 0, path); - return TRUE; - } - - return FALSE; -} - -/** - * gtk_cell_renderer_toggle_set_radio: - * @toggle: a `GtkCellRendererToggle` - * @radio: %TRUE to make the toggle look like a radio button - * - * If @radio is %TRUE, the cell renderer renders a radio toggle - * (i.e. a toggle in a group of mutually-exclusive toggles). - * If %FALSE, it renders a check toggle (a standalone boolean option). - * This can be set globally for the cell renderer, or changed just - * before rendering each cell in the model (for `GtkTreeView`, you set - * up a per-row setting using `GtkTreeViewColumn` to associate model - * columns with cell renderer properties). - **/ -void -gtk_cell_renderer_toggle_set_radio (GtkCellRendererToggle *toggle, - gboolean radio) -{ - GtkCellRendererTogglePrivate *priv = gtk_cell_renderer_toggle_get_instance_private (toggle); - - g_return_if_fail (GTK_IS_CELL_RENDERER_TOGGLE (toggle)); - - priv->radio = radio; -} - -/** - * gtk_cell_renderer_toggle_get_radio: - * @toggle: a `GtkCellRendererToggle` - * - * Returns whether we’re rendering radio toggles rather than checkboxes. - * - * Returns: %TRUE if we’re rendering radio toggles rather than checkboxes - **/ -gboolean -gtk_cell_renderer_toggle_get_radio (GtkCellRendererToggle *toggle) -{ - GtkCellRendererTogglePrivate *priv = gtk_cell_renderer_toggle_get_instance_private (toggle); - - g_return_val_if_fail (GTK_IS_CELL_RENDERER_TOGGLE (toggle), FALSE); - - return priv->radio; -} - -/** - * gtk_cell_renderer_toggle_get_active: - * @toggle: a `GtkCellRendererToggle` - * - * Returns whether the cell renderer is active. See - * gtk_cell_renderer_toggle_set_active(). - * - * Returns: %TRUE if the cell renderer is active. - **/ -gboolean -gtk_cell_renderer_toggle_get_active (GtkCellRendererToggle *toggle) -{ - GtkCellRendererTogglePrivate *priv = gtk_cell_renderer_toggle_get_instance_private (toggle); - - g_return_val_if_fail (GTK_IS_CELL_RENDERER_TOGGLE (toggle), FALSE); - - return priv->active; -} - -/** - * gtk_cell_renderer_toggle_set_active: - * @toggle: a `GtkCellRendererToggle`. - * @setting: the value to set. - * - * Activates or deactivates a cell renderer. - **/ -void -gtk_cell_renderer_toggle_set_active (GtkCellRendererToggle *toggle, - gboolean setting) -{ - g_return_if_fail (GTK_IS_CELL_RENDERER_TOGGLE (toggle)); - - g_object_set (toggle, "active", setting ? TRUE : FALSE, NULL); -} - -/** - * gtk_cell_renderer_toggle_get_activatable: - * @toggle: a `GtkCellRendererToggle` - * - * Returns whether the cell renderer is activatable. See - * gtk_cell_renderer_toggle_set_activatable(). - * - * Returns: %TRUE if the cell renderer is activatable. - **/ -gboolean -gtk_cell_renderer_toggle_get_activatable (GtkCellRendererToggle *toggle) -{ - GtkCellRendererTogglePrivate *priv = gtk_cell_renderer_toggle_get_instance_private (toggle); - - g_return_val_if_fail (GTK_IS_CELL_RENDERER_TOGGLE (toggle), FALSE); - - return priv->activatable; -} - -/** - * gtk_cell_renderer_toggle_set_activatable: - * @toggle: a `GtkCellRendererToggle`. - * @setting: the value to set. - * - * Makes the cell renderer activatable. - **/ -void -gtk_cell_renderer_toggle_set_activatable (GtkCellRendererToggle *toggle, - gboolean setting) -{ - GtkCellRendererTogglePrivate *priv = gtk_cell_renderer_toggle_get_instance_private (toggle); - - g_return_if_fail (GTK_IS_CELL_RENDERER_TOGGLE (toggle)); - - if (priv->activatable != setting) - { - priv->activatable = setting ? TRUE : FALSE; - g_object_notify (G_OBJECT (toggle), "activatable"); - } -} diff --git a/gtk/gtkcellrenderertoggle.h b/gtk/gtkcellrenderertoggle.h deleted file mode 100644 index 7cc80afa2e..0000000000 --- a/gtk/gtkcellrenderertoggle.h +++ /dev/null @@ -1,65 +0,0 @@ -/* gtkcellrenderertoggle.h - * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#ifndef __GTK_CELL_RENDERER_TOGGLE_H__ -#define __GTK_CELL_RENDERER_TOGGLE_H__ - -#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) -#error "Only can be included directly." -#endif - -#include - - -G_BEGIN_DECLS - - -#define GTK_TYPE_CELL_RENDERER_TOGGLE (gtk_cell_renderer_toggle_get_type ()) -#define GTK_CELL_RENDERER_TOGGLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_RENDERER_TOGGLE, GtkCellRendererToggle)) -#define GTK_IS_CELL_RENDERER_TOGGLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_RENDERER_TOGGLE)) - -typedef struct _GtkCellRendererToggle GtkCellRendererToggle; - - -GDK_AVAILABLE_IN_ALL -GType gtk_cell_renderer_toggle_get_type (void) G_GNUC_CONST; -GDK_AVAILABLE_IN_ALL -GtkCellRenderer *gtk_cell_renderer_toggle_new (void); - -GDK_AVAILABLE_IN_ALL -gboolean gtk_cell_renderer_toggle_get_radio (GtkCellRendererToggle *toggle); -GDK_AVAILABLE_IN_ALL -void gtk_cell_renderer_toggle_set_radio (GtkCellRendererToggle *toggle, - gboolean radio); - -GDK_AVAILABLE_IN_ALL -gboolean gtk_cell_renderer_toggle_get_active (GtkCellRendererToggle *toggle); -GDK_AVAILABLE_IN_ALL -void gtk_cell_renderer_toggle_set_active (GtkCellRendererToggle *toggle, - gboolean setting); - -GDK_AVAILABLE_IN_ALL -gboolean gtk_cell_renderer_toggle_get_activatable (GtkCellRendererToggle *toggle); -GDK_AVAILABLE_IN_ALL -void gtk_cell_renderer_toggle_set_activatable (GtkCellRendererToggle *toggle, - gboolean setting); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkCellRendererToggle, g_object_unref) - -G_END_DECLS - -#endif /* __GTK_CELL_RENDERER_TOGGLE_H__ */ diff --git a/gtk/gtkcellview.c b/gtk/gtkcellview.c deleted file mode 100644 index 3cb455c120..0000000000 --- a/gtk/gtkcellview.c +++ /dev/null @@ -1,1174 +0,0 @@ -/* gtkellview.c - * Copyright (C) 2002, 2003 Kristian Rietveld - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#include "config.h" - -#include "gtkcellview.h" - -#include "gtkbuildable.h" -#include "gtkcelllayout.h" -#include "gtkcellareabox.h" -#include "gtkcellrendererpixbuf.h" -#include "gtkcellrenderertext.h" -#include "gtkorientable.h" -#include "gtkprivate.h" -#include "gtkwidgetprivate.h" - -#include - -#include - -/** - * GtkCellView: - * - * A widget displaying a single row of a GtkTreeModel - * - * A `GtkCellView` displays a single row of a `GtkTreeModel` using a `GtkCellArea` - * and `GtkCellAreaContext`. A `GtkCellAreaContext` can be provided to the - * `GtkCellView` at construction time in order to keep the cellview in context - * of a group of cell views, this ensures that the renderers displayed will - * be properly aligned with each other (like the aligned cells in the menus - * of `GtkComboBox`). - * - * `GtkCellView` is `GtkOrientable` in order to decide in which orientation - * the underlying `GtkCellAreaContext` should be allocated. Taking the `GtkComboBox` - * menu as an example, cellviews should be oriented horizontally if the menus are - * listed top-to-bottom and thus all share the same width but may have separate - * individual heights (left-to-right menus should be allocated vertically since - * they all share the same height but may have variable widths). - * - * # CSS nodes - * - * GtkCellView has a single CSS node with name cellview. - */ - -static void gtk_cell_view_constructed (GObject *object); -static void gtk_cell_view_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec); -static void gtk_cell_view_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec); -static void gtk_cell_view_finalize (GObject *object); -static void gtk_cell_view_dispose (GObject *object); -static void gtk_cell_view_size_allocate (GtkWidget *widget, - int width, - int height, - int baseline); -static void gtk_cell_view_snapshot (GtkWidget *widget, - GtkSnapshot *snapshot); -static void gtk_cell_view_set_value (GtkCellView *cell_view, - GtkCellRenderer *renderer, - const char *property, - GValue *value); -static void gtk_cell_view_set_cell_data (GtkCellView *cell_view); - -/* celllayout */ -static void gtk_cell_view_cell_layout_init (GtkCellLayoutIface *iface); -static GtkCellArea *gtk_cell_view_cell_layout_get_area (GtkCellLayout *layout); - - -/* buildable */ -static void gtk_cell_view_buildable_init (GtkBuildableIface *iface); -static gboolean gtk_cell_view_buildable_custom_tag_start (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const char *tagname, - GtkBuildableParser *parser, - gpointer *data); -static void gtk_cell_view_buildable_custom_tag_end (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const char *tagname, - gpointer data); - -static GtkSizeRequestMode gtk_cell_view_get_request_mode (GtkWidget *widget); -static void gtk_cell_view_measure (GtkWidget *widget, - GtkOrientation orientation, - int for_size, - int *minimum, - int *natural, - int *minimum_baseline, - int *natural_baseline); -static void context_size_changed_cb (GtkCellAreaContext *context, - GParamSpec *pspec, - GtkWidget *view); -static void row_changed_cb (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - GtkCellView *view); - -typedef struct _GtkCellViewClass GtkCellViewClass; -typedef struct _GtkCellViewPrivate GtkCellViewPrivate; - -struct _GtkCellView -{ - GtkWidget parent_instance; -}; - -struct _GtkCellViewClass -{ - GtkWidgetClass parent_class; -}; - -struct _GtkCellViewPrivate -{ - GtkTreeModel *model; - GtkTreeRowReference *displayed_row; - - GtkCellArea *area; - GtkCellAreaContext *context; - - gulong size_changed_id; - gulong row_changed_id; - - GtkOrientation orientation; - - guint draw_sensitive : 1; - guint fit_model : 1; -}; - -static GtkBuildableIface *parent_buildable_iface; - -enum -{ - PROP_0, - PROP_ORIENTATION, - PROP_MODEL, - PROP_CELL_AREA, - PROP_CELL_AREA_CONTEXT, - PROP_DRAW_SENSITIVE, - PROP_FIT_MODEL -}; - -G_DEFINE_TYPE_WITH_CODE (GtkCellView, gtk_cell_view, GTK_TYPE_WIDGET, - G_ADD_PRIVATE (GtkCellView) - G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT, - gtk_cell_view_cell_layout_init) - G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, - gtk_cell_view_buildable_init) - G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL)) - -static void -gtk_cell_view_class_init (GtkCellViewClass *klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - - gobject_class->constructed = gtk_cell_view_constructed; - gobject_class->get_property = gtk_cell_view_get_property; - gobject_class->set_property = gtk_cell_view_set_property; - gobject_class->finalize = gtk_cell_view_finalize; - gobject_class->dispose = gtk_cell_view_dispose; - - widget_class->snapshot = gtk_cell_view_snapshot; - widget_class->size_allocate = gtk_cell_view_size_allocate; - widget_class->get_request_mode = gtk_cell_view_get_request_mode; - widget_class->measure = gtk_cell_view_measure; - - /* properties */ - g_object_class_override_property (gobject_class, PROP_ORIENTATION, "orientation"); - - /** - * GtkCellView:model: - * - * The model for cell view - * - * since 2.10 - */ - g_object_class_install_property (gobject_class, - PROP_MODEL, - g_param_spec_object ("model", NULL, NULL, - GTK_TYPE_TREE_MODEL, - GTK_PARAM_READWRITE)); - - - /** - * GtkCellView:cell-area: - * - * The `GtkCellArea` rendering cells - * - * If no area is specified when creating the cell view with gtk_cell_view_new_with_context() - * a horizontally oriented `GtkCellArea`Box will be used. - * - * since 3.0 - */ - g_object_class_install_property (gobject_class, - PROP_CELL_AREA, - g_param_spec_object ("cell-area", NULL, NULL, - GTK_TYPE_CELL_AREA, - GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - - /** - * GtkCellView:cell-area-context: - * - * The `GtkCellAreaContext` used to compute the geometry of the cell view. - * - * A group of cell views can be assigned the same context in order to - * ensure the sizes and cell alignments match across all the views with - * the same context. - * - * `GtkComboBox` menus uses this to assign the same context to all cell views - * in the menu items for a single menu (each submenu creates its own - * context since the size of each submenu does not depend on parent - * or sibling menus). - * - * since 3.0 - */ - g_object_class_install_property (gobject_class, - PROP_CELL_AREA_CONTEXT, - g_param_spec_object ("cell-area-context", NULL, NULL, - GTK_TYPE_CELL_AREA_CONTEXT, - GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - - /** - * GtkCellView:draw-sensitive: - * - * Whether all cells should be draw as sensitive for this view regardless - * of the actual cell properties (used to make menus with submenus appear - * sensitive when the items in submenus might be insensitive). - * - * since 3.0 - */ - g_object_class_install_property (gobject_class, - PROP_DRAW_SENSITIVE, - g_param_spec_boolean ("draw-sensitive", NULL, NULL, - FALSE, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - /** - * GtkCellView:fit-model: - * - * Whether the view should request enough space to always fit - * the size of every row in the model (used by the combo box to - * ensure the combo box size doesn't change when different items - * are selected). - * - * since 3.0 - */ - g_object_class_install_property (gobject_class, - PROP_FIT_MODEL, - g_param_spec_boolean ("fit-model", NULL, NULL, - FALSE, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - gtk_widget_class_set_css_name (widget_class, I_("cellview")); -} - -static void -gtk_cell_view_buildable_add_child (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const char *type) -{ - if (GTK_IS_CELL_RENDERER (child)) - _gtk_cell_layout_buildable_add_child (buildable, builder, child, type); - else - parent_buildable_iface->add_child (buildable, builder, child, type); -} - -static void -gtk_cell_view_buildable_init (GtkBuildableIface *iface) -{ - parent_buildable_iface = g_type_interface_peek_parent (iface); - iface->add_child = gtk_cell_view_buildable_add_child; - iface->custom_tag_start = gtk_cell_view_buildable_custom_tag_start; - iface->custom_tag_end = gtk_cell_view_buildable_custom_tag_end; -} - -static void -gtk_cell_view_cell_layout_init (GtkCellLayoutIface *iface) -{ - iface->get_area = gtk_cell_view_cell_layout_get_area; -} - -static void -gtk_cell_view_constructed (GObject *object) -{ - GtkCellView *view = GTK_CELL_VIEW (object); - GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (view); - - G_OBJECT_CLASS (gtk_cell_view_parent_class)->constructed (object); - - if (!priv->area) - { - priv->area = gtk_cell_area_box_new (); - g_object_ref_sink (priv->area); - } - - if (!priv->context) - priv->context = gtk_cell_area_create_context (priv->area); - - priv->size_changed_id = - g_signal_connect (priv->context, "notify", - G_CALLBACK (context_size_changed_cb), view); -} - -static void -gtk_cell_view_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec) -{ - GtkCellView *view = GTK_CELL_VIEW (object); - GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (view); - - switch (param_id) - { - case PROP_ORIENTATION: - g_value_set_enum (value, priv->orientation); - break; - case PROP_MODEL: - g_value_set_object (value, priv->model); - break; - case PROP_CELL_AREA: - g_value_set_object (value, priv->area); - break; - case PROP_CELL_AREA_CONTEXT: - g_value_set_object (value, priv->context); - break; - case PROP_DRAW_SENSITIVE: - g_value_set_boolean (value, priv->draw_sensitive); - break; - case PROP_FIT_MODEL: - g_value_set_boolean (value, priv->fit_model); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); - break; - } -} - -static void -gtk_cell_view_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec) -{ - GtkCellView *view = GTK_CELL_VIEW (object); - GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (view); - GtkCellArea *area; - GtkCellAreaContext *context; - - switch (param_id) - { - case PROP_ORIENTATION: - if (priv->orientation != g_value_get_enum (value)) - { - priv->orientation = g_value_get_enum (value); - if (priv->context) - gtk_cell_area_context_reset (priv->context); - gtk_widget_update_orientation (GTK_WIDGET (object), priv->orientation); - g_object_notify_by_pspec (object, pspec); - } - break; - case PROP_MODEL: - gtk_cell_view_set_model (view, g_value_get_object (value)); - break; - case PROP_CELL_AREA: - /* Construct-only, can only be assigned once */ - area = g_value_get_object (value); - - if (area) - { - if (priv->area != NULL) - { - g_warning ("cell-area has already been set, ignoring construct property"); - g_object_ref_sink (area); - g_object_unref (area); - } - else - priv->area = g_object_ref_sink (area); - } - break; - case PROP_CELL_AREA_CONTEXT: - /* Construct-only, can only be assigned once */ - context = g_value_get_object (value); - - if (context) - { - if (priv->context != NULL) - { - g_warning ("cell-area-context has already been set, ignoring construct property"); - g_object_ref_sink (context); - g_object_unref (context); - } - else - priv->context = g_object_ref (context); - } - break; - - case PROP_DRAW_SENSITIVE: - gtk_cell_view_set_draw_sensitive (view, g_value_get_boolean (value)); - break; - - case PROP_FIT_MODEL: - gtk_cell_view_set_fit_model (view, g_value_get_boolean (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); - break; - } -} - -static void -gtk_cell_view_init (GtkCellView *cellview) -{ - GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (cellview); - - priv->orientation = GTK_ORIENTATION_HORIZONTAL; -} - -static void -gtk_cell_view_finalize (GObject *object) -{ - GtkCellView *cellview = GTK_CELL_VIEW (object); - GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (cellview); - - if (priv->displayed_row) - gtk_tree_row_reference_free (priv->displayed_row); - - G_OBJECT_CLASS (gtk_cell_view_parent_class)->finalize (object); -} - -static void -gtk_cell_view_dispose (GObject *object) -{ - GtkCellView *cellview = GTK_CELL_VIEW (object); - GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (cellview); - - gtk_cell_view_set_model (cellview, NULL); - - g_clear_object (&priv->area); - - if (priv->context) - { - g_signal_handler_disconnect (priv->context, priv->size_changed_id); - - g_object_unref (priv->context); - priv->context = NULL; - priv->size_changed_id = 0; - } - - G_OBJECT_CLASS (gtk_cell_view_parent_class)->dispose (object); -} - -static void -gtk_cell_view_size_allocate (GtkWidget *widget, - int width, - int height, - int baseline) -{ - GtkCellView *cellview = GTK_CELL_VIEW (widget); - GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (cellview); - int alloc_width, alloc_height; - - gtk_cell_area_context_get_allocation (priv->context, &alloc_width, &alloc_height); - - /* The first cell view in context is responsible for allocating the context at - * allocate time (or the cellview has its own context and is not grouped with - * any other cell views) - * - * If the cellview is in "fit model" mode, we assume it's not in context and - * needs to allocate every time. - */ - if (priv->fit_model) - gtk_cell_area_context_allocate (priv->context, width, height); - else if (alloc_width != width && priv->orientation == GTK_ORIENTATION_HORIZONTAL) - gtk_cell_area_context_allocate (priv->context, width, -1); - else if (alloc_height != height && priv->orientation == GTK_ORIENTATION_VERTICAL) - gtk_cell_area_context_allocate (priv->context, -1, height); -} - -static void -gtk_cell_view_request_model (GtkCellView *cellview, - GtkTreeIter *parent, - GtkOrientation orientation, - int for_size, - int *minimum_size, - int *natural_size) -{ - GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (cellview); - GtkTreeIter iter; - gboolean valid; - - if (!priv->model) - return; - - valid = gtk_tree_model_iter_children (priv->model, &iter, parent); - while (valid) - { - int min, nat; - - gtk_cell_area_apply_attributes (priv->area, priv->model, &iter, FALSE, FALSE); - - if (orientation == GTK_ORIENTATION_HORIZONTAL) - { - if (for_size < 0) - gtk_cell_area_get_preferred_width (priv->area, priv->context, - GTK_WIDGET (cellview), &min, &nat); - else - gtk_cell_area_get_preferred_width_for_height (priv->area, priv->context, - GTK_WIDGET (cellview), for_size, &min, &nat); - } - else - { - if (for_size < 0) - gtk_cell_area_get_preferred_height (priv->area, priv->context, - GTK_WIDGET (cellview), &min, &nat); - else - gtk_cell_area_get_preferred_height_for_width (priv->area, priv->context, - GTK_WIDGET (cellview), for_size, &min, &nat); - } - - *minimum_size = MAX (min, *minimum_size); - *natural_size = MAX (nat, *natural_size); - - /* Recurse into children when they exist */ - gtk_cell_view_request_model (cellview, &iter, orientation, for_size, minimum_size, natural_size); - - valid = gtk_tree_model_iter_next (priv->model, &iter); - } -} - -static GtkSizeRequestMode -gtk_cell_view_get_request_mode (GtkWidget *widget) -{ - GtkCellView *cellview = GTK_CELL_VIEW (widget); - GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (cellview); - - return gtk_cell_area_get_request_mode (priv->area); -} - -static void -gtk_cell_view_measure (GtkWidget *widget, - GtkOrientation orientation, - int for_size, - int *minimum, - int *natural, - int *minimum_baseline, - int *natural_baseline) -{ - GtkCellView *cellview = GTK_CELL_VIEW (widget); - GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (cellview); - - g_signal_handler_block (priv->context, priv->size_changed_id); - - if (orientation == GTK_ORIENTATION_HORIZONTAL && for_size == -1) - { - if (priv->fit_model) - { - int min = 0, nat = 0; - gtk_cell_view_request_model (cellview, NULL, GTK_ORIENTATION_HORIZONTAL, -1, &min, &nat); - } - else - { - if (priv->displayed_row) - gtk_cell_view_set_cell_data (cellview); - - gtk_cell_area_get_preferred_width (priv->area, priv->context, widget, NULL, NULL); - } - - gtk_cell_area_context_get_preferred_width (priv->context, minimum, natural); - } - else if (orientation == GTK_ORIENTATION_VERTICAL && for_size == -1) - { - if (priv->fit_model) - { - int min = 0, nat = 0; - gtk_cell_view_request_model (cellview, NULL, GTK_ORIENTATION_VERTICAL, -1, &min, &nat); - } - else - { - if (priv->displayed_row) - gtk_cell_view_set_cell_data (cellview); - - gtk_cell_area_get_preferred_height (priv->area, priv->context, widget, NULL, NULL); - } - - gtk_cell_area_context_get_preferred_height (priv->context, minimum, natural); - } - else if (orientation == GTK_ORIENTATION_HORIZONTAL && for_size >= 0) - { - if (priv->fit_model) - { - int min = 0, nat = 0; - gtk_cell_view_request_model (cellview, NULL, GTK_ORIENTATION_HORIZONTAL, for_size, &min, &nat); - - *minimum = min; - *natural = nat; - } - else - { - if (priv->displayed_row) - gtk_cell_view_set_cell_data (cellview); - - gtk_cell_area_get_preferred_width_for_height (priv->area, priv->context, widget, - for_size, minimum, natural); - } - } - else - { - if (priv->fit_model) - { - int min = 0, nat = 0; - gtk_cell_view_request_model (cellview, NULL, GTK_ORIENTATION_VERTICAL, for_size, &min, &nat); - - *minimum = min; - *natural = nat; - } - else - { - if (priv->displayed_row) - gtk_cell_view_set_cell_data (cellview); - - gtk_cell_area_get_preferred_height_for_width (priv->area, priv->context, widget, - for_size, minimum, natural); - } - } - - g_signal_handler_unblock (priv->context, priv->size_changed_id); -} - -static void -gtk_cell_view_snapshot (GtkWidget *widget, - GtkSnapshot *snapshot) -{ - GtkCellView *cellview = GTK_CELL_VIEW (widget); - GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (cellview); - GdkRectangle area; - GtkCellRendererState state; - - /* render cells */ - area.x = 0; - area.y = 0; - area.width = gtk_widget_get_width (widget); - area.height = gtk_widget_get_height (widget); - - /* set cell data (if available) */ - if (priv->displayed_row) - gtk_cell_view_set_cell_data (cellview); - else if (priv->model) - return; - - if (gtk_widget_get_state_flags (widget) & GTK_STATE_FLAG_PRELIGHT) - state = GTK_CELL_RENDERER_PRELIT; - else - state = 0; - - /* Render the cells */ - gtk_cell_area_snapshot (priv->area, priv->context, - widget, snapshot, &area, &area, state, FALSE); - - -} - -static void -gtk_cell_view_set_cell_data (GtkCellView *cell_view) -{ - GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (cell_view); - GtkTreeIter iter; - GtkTreePath *path; - - g_return_if_fail (priv->displayed_row != NULL); - - path = gtk_tree_row_reference_get_path (priv->displayed_row); - if (!path) - return; - - gtk_tree_model_get_iter (priv->model, &iter, path); - gtk_tree_path_free (path); - - gtk_cell_area_apply_attributes (priv->area, - priv->model, - &iter, FALSE, FALSE); - - if (priv->draw_sensitive) - { - GList *l, *cells = - gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (priv->area)); - - for (l = cells; l; l = l->next) - { - GObject *renderer = l->data; - - g_object_set (renderer, "sensitive", TRUE, NULL); - } - g_list_free (cells); - } -} - -/* GtkCellLayout implementation */ -static GtkCellArea * -gtk_cell_view_cell_layout_get_area (GtkCellLayout *layout) -{ - GtkCellView *cellview = GTK_CELL_VIEW (layout); - GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (cellview); - - if (G_UNLIKELY (!priv->area)) - { - priv->area = gtk_cell_area_box_new (); - g_object_ref_sink (priv->area); - } - - return priv->area; -} - -/* GtkBuildable implementation */ -static gboolean -gtk_cell_view_buildable_custom_tag_start (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const char *tagname, - GtkBuildableParser *parser, - gpointer *data) -{ - if (parent_buildable_iface->custom_tag_start && - parent_buildable_iface->custom_tag_start (buildable, builder, child, - tagname, parser, data)) - return TRUE; - - return _gtk_cell_layout_buildable_custom_tag_start (buildable, builder, child, - tagname, parser, data); -} - -static void -gtk_cell_view_buildable_custom_tag_end (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const char *tagname, - gpointer data) -{ - if (_gtk_cell_layout_buildable_custom_tag_end (buildable, builder, child, tagname, - data)) - return; - else if (parent_buildable_iface->custom_tag_end) - parent_buildable_iface->custom_tag_end (buildable, builder, child, tagname, - data); -} - -static void -context_size_changed_cb (GtkCellAreaContext *context, - GParamSpec *pspec, - GtkWidget *view) -{ - if (!strcmp (pspec->name, "minimum-width") || - !strcmp (pspec->name, "natural-width") || - !strcmp (pspec->name, "minimum-height") || - !strcmp (pspec->name, "natural-height")) - gtk_widget_queue_resize (view); -} - -static void -row_changed_cb (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - GtkCellView *view) -{ - GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (view); - GtkTreePath *row_path; - - if (priv->displayed_row) - { - row_path = gtk_tree_row_reference_get_path (priv->displayed_row); - - if (row_path) - { - /* Resize everything in our context if our row changed */ - if (gtk_tree_path_compare (row_path, path) == 0) - gtk_cell_area_context_reset (priv->context); - - gtk_tree_path_free (row_path); - } - } -} - -/** - * gtk_cell_view_new: - * - * Creates a new `GtkCellView` widget. - * - * Returns: A newly created `GtkCellView` widget. - */ -GtkWidget * -gtk_cell_view_new (void) -{ - GtkCellView *cellview; - - cellview = g_object_new (GTK_TYPE_CELL_VIEW, NULL); - - return GTK_WIDGET (cellview); -} - - -/** - * gtk_cell_view_new_with_context: - * @area: the `GtkCellArea` to layout cells - * @context: the `GtkCellAreaContext` in which to calculate cell geometry - * - * Creates a new `GtkCellView` widget with a specific `GtkCellArea` - * to layout cells and a specific `GtkCellAreaContext`. - * - * Specifying the same context for a handful of cells lets - * the underlying area synchronize the geometry for those cells, - * in this way alignments with cellviews for other rows are - * possible. - * - * Returns: A newly created `GtkCellView` widget. - */ -GtkWidget * -gtk_cell_view_new_with_context (GtkCellArea *area, - GtkCellAreaContext *context) -{ - g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL); - g_return_val_if_fail (context == NULL || GTK_IS_CELL_AREA_CONTEXT (context), NULL); - - return (GtkWidget *)g_object_new (GTK_TYPE_CELL_VIEW, - "cell-area", area, - "cell-area-context", context, - NULL); -} - -/** - * gtk_cell_view_new_with_text: - * @text: the text to display in the cell view - * - * Creates a new `GtkCellView` widget, adds a `GtkCellRendererText` - * to it, and makes it show @text. - * - * Returns: A newly created `GtkCellView` widget. - */ -GtkWidget * -gtk_cell_view_new_with_text (const char *text) -{ - GtkCellView *cellview; - GtkCellRenderer *renderer; - GValue value = G_VALUE_INIT; - - cellview = GTK_CELL_VIEW (gtk_cell_view_new ()); - - renderer = gtk_cell_renderer_text_new (); - gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (cellview), - renderer, TRUE); - - g_value_init (&value, G_TYPE_STRING); - g_value_set_string (&value, text); - gtk_cell_view_set_value (cellview, renderer, "text", &value); - g_value_unset (&value); - - return GTK_WIDGET (cellview); -} - -/** - * gtk_cell_view_new_with_markup: - * @markup: the text to display in the cell view - * - * Creates a new `GtkCellView` widget, adds a `GtkCellRendererText` - * to it, and makes it show @markup. The text can be marked up with - * the [Pango text markup language](https://docs.gtk.org/Pango/pango_markup.html). - * - * Returns: A newly created `GtkCellView` widget. - */ -GtkWidget * -gtk_cell_view_new_with_markup (const char *markup) -{ - GtkCellView *cellview; - GtkCellRenderer *renderer; - GValue value = G_VALUE_INIT; - - cellview = GTK_CELL_VIEW (gtk_cell_view_new ()); - - renderer = gtk_cell_renderer_text_new (); - gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (cellview), - renderer, TRUE); - - g_value_init (&value, G_TYPE_STRING); - g_value_set_string (&value, markup); - gtk_cell_view_set_value (cellview, renderer, "markup", &value); - g_value_unset (&value); - - return GTK_WIDGET (cellview); -} - -/** - * gtk_cell_view_new_with_texture: - * @texture: the image to display in the cell view - * - * Creates a new `GtkCellView` widget, adds a `GtkCellRendererPixbuf` - * to it, and makes it show @texture. - * - * Returns: A newly created `GtkCellView` widget. - */ -GtkWidget * -gtk_cell_view_new_with_texture (GdkTexture *texture) -{ - GtkCellView *cellview; - GtkCellRenderer *renderer; - GValue value = G_VALUE_INIT; - - cellview = GTK_CELL_VIEW (gtk_cell_view_new ()); - - renderer = gtk_cell_renderer_pixbuf_new (); - gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (cellview), - renderer, TRUE); - - g_value_init (&value, GDK_TYPE_TEXTURE); - g_value_set_object (&value, texture); - gtk_cell_view_set_value (cellview, renderer, "texture", &value); - g_value_unset (&value); - - return GTK_WIDGET (cellview); -} - -/** - * gtk_cell_view_set_value: - * @cell_view: a `GtkCellView` widget - * @renderer: one of the renderers of @cell_view - * @property: the name of the property of @renderer to set - * @value: the new value to set the property to - * - * Sets a property of a cell renderer of @cell_view, and - * makes sure the display of @cell_view is updated. - */ -static void -gtk_cell_view_set_value (GtkCellView *cell_view, - GtkCellRenderer *renderer, - const char *property, - GValue *value) -{ - g_object_set_property (G_OBJECT (renderer), property, value); - - /* force resize and redraw */ - gtk_widget_queue_resize (GTK_WIDGET (cell_view)); - gtk_widget_queue_draw (GTK_WIDGET (cell_view)); -} - -/** - * gtk_cell_view_set_model: - * @cell_view: a `GtkCellView` - * @model: (nullable): a `GtkTreeModel` - * - * Sets the model for @cell_view. If @cell_view already has a model - * set, it will remove it before setting the new model. If @model is - * %NULL, then it will unset the old model. - */ -void -gtk_cell_view_set_model (GtkCellView *cell_view, - GtkTreeModel *model) -{ - GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (cell_view); - - g_return_if_fail (GTK_IS_CELL_VIEW (cell_view)); - g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model)); - - if (priv->model) - { - g_signal_handler_disconnect (priv->model, priv->row_changed_id); - priv->row_changed_id = 0; - - if (priv->displayed_row) - gtk_tree_row_reference_free (priv->displayed_row); - priv->displayed_row = NULL; - - g_object_unref (priv->model); - } - - priv->model = model; - - if (priv->model) - { - g_object_ref (priv->model); - - priv->row_changed_id = - g_signal_connect (priv->model, "row-changed", - G_CALLBACK (row_changed_cb), cell_view); - } -} - -/** - * gtk_cell_view_get_model: - * @cell_view: a `GtkCellView` - * - * Returns the model for @cell_view. If no model is used %NULL is - * returned. - * - * Returns: (nullable) (transfer none): a `GtkTreeModel` used - */ -GtkTreeModel * -gtk_cell_view_get_model (GtkCellView *cell_view) -{ - GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (cell_view); - - g_return_val_if_fail (GTK_IS_CELL_VIEW (cell_view), NULL); - - return priv->model; -} - -/** - * gtk_cell_view_set_displayed_row: - * @cell_view: a `GtkCellView` - * @path: (nullable): a `GtkTreePath` or %NULL to unset. - * - * Sets the row of the model that is currently displayed - * by the `GtkCellView`. If the path is unset, then the - * contents of the cellview “stick” at their last value; - * this is not normally a desired result, but may be - * a needed intermediate state if say, the model for - * the `GtkCellView` becomes temporarily empty. - **/ -void -gtk_cell_view_set_displayed_row (GtkCellView *cell_view, - GtkTreePath *path) -{ - GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (cell_view); - - g_return_if_fail (GTK_IS_CELL_VIEW (cell_view)); - g_return_if_fail (GTK_IS_TREE_MODEL (priv->model)); - - if (priv->displayed_row) - gtk_tree_row_reference_free (priv->displayed_row); - - if (path) - priv->displayed_row = gtk_tree_row_reference_new (priv->model, path); - else - priv->displayed_row = NULL; - - /* force resize and redraw */ - gtk_widget_queue_resize (GTK_WIDGET (cell_view)); - gtk_widget_queue_draw (GTK_WIDGET (cell_view)); -} - -/** - * gtk_cell_view_get_displayed_row: - * @cell_view: a `GtkCellView` - * - * Returns a `GtkTreePath` referring to the currently - * displayed row. If no row is currently displayed, - * %NULL is returned. - * - * Returns: (nullable) (transfer full): the currently displayed row - */ -GtkTreePath * -gtk_cell_view_get_displayed_row (GtkCellView *cell_view) -{ - GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (cell_view); - - g_return_val_if_fail (GTK_IS_CELL_VIEW (cell_view), NULL); - - if (!priv->displayed_row) - return NULL; - - return gtk_tree_row_reference_get_path (priv->displayed_row); -} - -/** - * gtk_cell_view_get_draw_sensitive: - * @cell_view: a `GtkCellView` - * - * Gets whether @cell_view is configured to draw all of its - * cells in a sensitive state. - * - * Returns: whether @cell_view draws all of its - * cells in a sensitive state - */ -gboolean -gtk_cell_view_get_draw_sensitive (GtkCellView *cell_view) -{ - GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (cell_view); - - g_return_val_if_fail (GTK_IS_CELL_VIEW (cell_view), FALSE); - - return priv->draw_sensitive; -} - -/** - * gtk_cell_view_set_draw_sensitive: - * @cell_view: a `GtkCellView` - * @draw_sensitive: whether to draw all cells in a sensitive state. - * - * Sets whether @cell_view should draw all of its - * cells in a sensitive state, this is used by `GtkComboBox` menus - * to ensure that rows with insensitive cells that contain - * children appear sensitive in the parent menu item. - */ -void -gtk_cell_view_set_draw_sensitive (GtkCellView *cell_view, - gboolean draw_sensitive) -{ - GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (cell_view); - - g_return_if_fail (GTK_IS_CELL_VIEW (cell_view)); - - if (priv->draw_sensitive != draw_sensitive) - { - priv->draw_sensitive = draw_sensitive; - - g_object_notify (G_OBJECT (cell_view), "draw-sensitive"); - } -} - -/** - * gtk_cell_view_get_fit_model: - * @cell_view: a `GtkCellView` - * - * Gets whether @cell_view is configured to request space - * to fit the entire `GtkTreeModel`. - * - * Returns: whether @cell_view requests space to fit - * the entire `GtkTreeModel`. - */ -gboolean -gtk_cell_view_get_fit_model (GtkCellView *cell_view) -{ - GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (cell_view); - - g_return_val_if_fail (GTK_IS_CELL_VIEW (cell_view), FALSE); - - return priv->fit_model; -} - -/** - * gtk_cell_view_set_fit_model: - * @cell_view: a `GtkCellView` - * @fit_model: whether @cell_view should request space for the whole model. - * - * Sets whether @cell_view should request space to fit the entire `GtkTreeModel`. - * - * This is used by `GtkComboBox` to ensure that the cell view displayed on - * the combo box’s button always gets enough space and does not resize - * when selection changes. - */ -void -gtk_cell_view_set_fit_model (GtkCellView *cell_view, - gboolean fit_model) -{ - GtkCellViewPrivate *priv = gtk_cell_view_get_instance_private (cell_view); - - g_return_if_fail (GTK_IS_CELL_VIEW (cell_view)); - - if (priv->fit_model != fit_model) - { - priv->fit_model = fit_model; - - gtk_cell_area_context_reset (priv->context); - - g_object_notify (G_OBJECT (cell_view), "fit-model"); - } -} diff --git a/gtk/gtkcellview.h b/gtk/gtkcellview.h deleted file mode 100644 index 77c131c46e..0000000000 --- a/gtk/gtkcellview.h +++ /dev/null @@ -1,77 +0,0 @@ -/* gtkcellview.h - * Copyright (C) 2002, 2003 Kristian Rietveld - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#ifndef __GTK_CELL_VIEW_H__ -#define __GTK_CELL_VIEW_H__ - -#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) -#error "Only can be included directly." -#endif - -#include -#include -#include -#include -#include - -G_BEGIN_DECLS - -#define GTK_TYPE_CELL_VIEW (gtk_cell_view_get_type ()) -#define GTK_CELL_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_VIEW, GtkCellView)) -#define GTK_IS_CELL_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_VIEW)) - -typedef struct _GtkCellView GtkCellView; - -GDK_AVAILABLE_IN_ALL -GType gtk_cell_view_get_type (void) G_GNUC_CONST; -GDK_AVAILABLE_IN_ALL -GtkWidget *gtk_cell_view_new (void); -GDK_AVAILABLE_IN_ALL -GtkWidget *gtk_cell_view_new_with_context (GtkCellArea *area, - GtkCellAreaContext *context); -GDK_AVAILABLE_IN_ALL -GtkWidget *gtk_cell_view_new_with_text (const char *text); -GDK_AVAILABLE_IN_ALL -GtkWidget *gtk_cell_view_new_with_markup (const char *markup); -GDK_AVAILABLE_IN_ALL -GtkWidget *gtk_cell_view_new_with_texture (GdkTexture *texture); -GDK_AVAILABLE_IN_ALL -void gtk_cell_view_set_model (GtkCellView *cell_view, - GtkTreeModel *model); -GDK_AVAILABLE_IN_ALL -GtkTreeModel *gtk_cell_view_get_model (GtkCellView *cell_view); -GDK_AVAILABLE_IN_ALL -void gtk_cell_view_set_displayed_row (GtkCellView *cell_view, - GtkTreePath *path); -GDK_AVAILABLE_IN_ALL -GtkTreePath *gtk_cell_view_get_displayed_row (GtkCellView *cell_view); -GDK_AVAILABLE_IN_ALL -gboolean gtk_cell_view_get_draw_sensitive (GtkCellView *cell_view); -GDK_AVAILABLE_IN_ALL -void gtk_cell_view_set_draw_sensitive (GtkCellView *cell_view, - gboolean draw_sensitive); -GDK_AVAILABLE_IN_ALL -gboolean gtk_cell_view_get_fit_model (GtkCellView *cell_view); -GDK_AVAILABLE_IN_ALL -void gtk_cell_view_set_fit_model (GtkCellView *cell_view, - gboolean fit_model); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkCellView, g_object_unref) - -G_END_DECLS - -#endif /* __GTK_CELL_VIEW_H__ */ diff --git a/gtk/gtkcombobox.c b/gtk/gtkcombobox.c deleted file mode 100644 index 09a36bfa1e..0000000000 --- a/gtk/gtkcombobox.c +++ /dev/null @@ -1,3016 +0,0 @@ -/* gtkcombobox.c - * Copyright (C) 2002, 2003 Kristian Rietveld - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#include "config.h" - -#include "gtkcomboboxprivate.h" - -#include "gtkbox.h" -#include "gtkcellareabox.h" -#include "gtkcelllayout.h" -#include "gtkcellrenderertext.h" -#include "gtkcellview.h" -#include "gtkeventcontrollerkey.h" -#include "gtkeventcontrollerscroll.h" -#include "gtkframe.h" -#include "gtkbuiltiniconprivate.h" -#include "gtkliststore.h" -#include "gtkmain.h" -#include "gtkmarshalers.h" -#include "gtkprivate.h" -#include "gtkshortcutcontroller.h" -#include "gtktogglebutton.h" -#include "gtktreepopoverprivate.h" -#include "gtktypebuiltins.h" -#include "gtkwidgetprivate.h" - -#include -#include -#include - -/** - * GtkComboBox: - * - * A `GtkComboBox` is a widget that allows the user to choose from a list of - * valid choices. - * - * ![An example GtkComboBox](combo-box.png) - * - * The `GtkComboBox` displays the selected choice; when activated, the - * `GtkComboBox` displays a popup which allows the user to make a new choice. - * - * The `GtkComboBox` uses the model-view pattern; the list of valid choices - * is specified in the form of a tree model, and the display of the choices - * can be adapted to the data in the model by using cell renderers, as you - * would in a tree view. This is possible since `GtkComboBox` implements the - * [iface@Gtk.CellLayout] interface. The tree model holding the valid - * choices is not restricted to a flat list, it can be a real tree, and the - * popup will reflect the tree structure. - * - * To allow the user to enter values not in the model, the - * [property@Gtk.ComboBox:has-entry] property allows the `GtkComboBox` to - * contain a [class@Gtk.Entry]. This entry can be accessed by calling - * [method@Gtk.ComboBox.get_child] on the combo box. - * - * For a simple list of textual choices, the model-view API of `GtkComboBox` - * can be a bit overwhelming. In this case, [class@Gtk.ComboBoxText] offers - * a simple alternative. Both `GtkComboBox` and `GtkComboBoxText` can contain - * an entry. - * - * ## CSS nodes - * - * ``` - * combobox - * ├── box.linked - * │ ╰── button.combo - * │ ╰── box - * │ ├── cellview - * │ ╰── arrow - * ╰── window.popup - * ``` - * - * A normal combobox contains a box with the .linked class, a button - * with the .combo class and inside those buttons, there are a cellview and - * an arrow. - * - * ``` - * combobox - * ├── box.linked - * │ ├── entry.combo - * │ ╰── button.combo - * │ ╰── box - * │ ╰── arrow - * ╰── window.popup - * ``` - * - * A `GtkComboBox` with an entry has a single CSS node with name combobox. - * It contains a box with the .linked class. That box contains an entry and - * a button, both with the .combo class added. The button also contains another - * node with name arrow. - * - * # Accessibility - * - * `GtkComboBox` uses the %GTK_ACCESSIBLE_ROLE_COMBO_BOX role. - */ - -typedef struct -{ - GtkWidget *child; - - GtkTreeModel *model; - - GtkCellArea *area; - - int active; /* Only temporary */ - GtkTreeRowReference *active_row; - - GtkWidget *cell_view; - - GtkWidget *box; - GtkWidget *button; - GtkWidget *arrow; - - GtkWidget *popup_widget; - - guint popup_idle_id; - guint scroll_timer; - guint resize_idle_id; - - /* For "has-entry" specific behavior we track - * an automated cell renderer and text column - */ - int text_column; - GtkCellRenderer *text_renderer; - - int id_column; - - guint popup_in_progress : 1; - guint popup_shown : 1; - guint has_frame : 1; - guint is_cell_renderer : 1; - guint editing_canceled : 1; - guint auto_scroll : 1; - guint button_sensitivity : 2; - guint has_entry : 1; - guint popup_fixed_width : 1; - - GtkTreeViewRowSeparatorFunc row_separator_func; - gpointer row_separator_data; - GDestroyNotify row_separator_destroy; -} GtkComboBoxPrivate; - -/* There are 2 modes to this widget, which can be characterized as follows: - * - * 1) no child added: - * - * cell_view -> GtkCellView, regular child - * button -> GtkToggleButton set_parent to combo - * arrow -> GtkArrow set_parent to button - * popup_widget -> GtkMenu - * - * 2) child added: - * - * cell_view -> NULL - * button -> GtkToggleButton set_parent to combo - * arrow -> GtkArrow, child of button - * popup_widget -> GtkMenu - */ - -enum { - ACTIVATE, - CHANGED, - MOVE_ACTIVE, - POPUP, - POPDOWN, - FORMAT_ENTRY_TEXT, - LAST_SIGNAL -}; - -enum { - PROP_0, - PROP_MODEL, - PROP_ACTIVE, - PROP_HAS_FRAME, - PROP_POPUP_SHOWN, - PROP_BUTTON_SENSITIVITY, - PROP_EDITING_CANCELED, - PROP_HAS_ENTRY, - PROP_ENTRY_TEXT_COLUMN, - PROP_POPUP_FIXED_WIDTH, - PROP_ID_COLUMN, - PROP_ACTIVE_ID, - PROP_CHILD -}; - -static guint combo_box_signals[LAST_SIGNAL] = {0,}; - -/* common */ - -static void gtk_combo_box_cell_layout_init (GtkCellLayoutIface *iface); -static void gtk_combo_box_cell_editable_init (GtkCellEditableIface *iface); -static void gtk_combo_box_constructed (GObject *object); -static void gtk_combo_box_dispose (GObject *object); -static void gtk_combo_box_unmap (GtkWidget *widget); - -static void gtk_combo_box_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *spec); -static void gtk_combo_box_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *spec); - -static gboolean gtk_combo_box_grab_focus (GtkWidget *widget); -static void gtk_combo_box_button_toggled (GtkWidget *widget, - gpointer data); - -static void gtk_combo_box_menu_show (GtkWidget *menu, - gpointer user_data); -static void gtk_combo_box_menu_hide (GtkWidget *menu, - gpointer user_data); - -static void gtk_combo_box_unset_model (GtkComboBox *combo_box); - -static void gtk_combo_box_set_active_internal (GtkComboBox *combo_box, - GtkTreePath *path); - -static void gtk_combo_box_real_move_active (GtkComboBox *combo_box, - GtkScrollType scroll); -static void gtk_combo_box_real_popup (GtkComboBox *combo_box); -static gboolean gtk_combo_box_real_popdown (GtkComboBox *combo_box); - -static gboolean gtk_combo_box_scroll_controller_scroll (GtkEventControllerScroll *scroll, - double dx, - double dy, - GtkComboBox *combo_box); - -/* listening to the model */ -static void gtk_combo_box_model_row_inserted (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer user_data); -static void gtk_combo_box_model_row_deleted (GtkTreeModel *model, - GtkTreePath *path, - gpointer user_data); -static void gtk_combo_box_model_rows_reordered (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - int *new_order, - gpointer user_data); -static void gtk_combo_box_model_row_changed (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data); - -static void gtk_combo_box_menu_activate (GtkWidget *menu, - const char *path, - GtkComboBox *combo_box); -static void gtk_combo_box_update_sensitivity (GtkComboBox *combo_box); -static gboolean gtk_combo_box_menu_key (GtkEventControllerKey *key, - guint keyval, - guint keycode, - GdkModifierType modifiers, - GtkComboBox *combo_box); -static void gtk_combo_box_menu_popup (GtkComboBox *combo_box); - -/* cell layout */ -static GtkCellArea *gtk_combo_box_cell_layout_get_area (GtkCellLayout *cell_layout); - -static gboolean gtk_combo_box_mnemonic_activate (GtkWidget *widget, - gboolean group_cycling); - -static void gtk_combo_box_child_show (GtkWidget *widget, - GtkComboBox *combo_box); -static void gtk_combo_box_child_hide (GtkWidget *widget, - GtkComboBox *combo_box); - -/* GtkComboBox:has-entry callbacks */ -static void gtk_combo_box_entry_contents_changed (GtkEntry *entry, - gpointer user_data); -static void gtk_combo_box_entry_active_changed (GtkComboBox *combo_box, - gpointer user_data); -static char *gtk_combo_box_format_entry_text (GtkComboBox *combo_box, - const char *path); - -/* GtkBuildable method implementation */ -static GtkBuildableIface *parent_buildable_iface; - -static void gtk_combo_box_buildable_init (GtkBuildableIface *iface); -static void gtk_combo_box_buildable_add_child (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const char *type); -static gboolean gtk_combo_box_buildable_custom_tag_start (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const char *tagname, - GtkBuildableParser *parser, - gpointer *data); -static void gtk_combo_box_buildable_custom_tag_end (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const char *tagname, - gpointer data); -static GObject *gtk_combo_box_buildable_get_internal_child (GtkBuildable *buildable, - GtkBuilder *builder, - const char *childname); - - - -/* GtkCellEditable method implementations */ -static void gtk_combo_box_start_editing (GtkCellEditable *cell_editable, - GdkEvent *event); - -G_DEFINE_TYPE_WITH_CODE (GtkComboBox, gtk_combo_box, GTK_TYPE_WIDGET, - G_ADD_PRIVATE (GtkComboBox) - G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT, - gtk_combo_box_cell_layout_init) - G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_EDITABLE, - gtk_combo_box_cell_editable_init) - G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, - gtk_combo_box_buildable_init)) - - -/* common */ -static void -gtk_combo_box_measure (GtkWidget *widget, - GtkOrientation orientation, - int size, - int *minimum, - int *natural, - int *minimum_baseline, - int *natural_baseline) -{ - GtkComboBox *combo_box = GTK_COMBO_BOX (widget); - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - - gtk_widget_measure (priv->box, - orientation, - size, - minimum, natural, - minimum_baseline, natural_baseline); -} - -static void -gtk_combo_box_activate (GtkComboBox *combo_box) -{ - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - - gtk_widget_activate (priv->button); -} - -static void -gtk_combo_box_size_allocate (GtkWidget *widget, - int width, - int height, - int baseline) -{ - GtkComboBox *combo_box = GTK_COMBO_BOX (widget); - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - int menu_width; - - gtk_widget_size_allocate (priv->box, - &(GtkAllocation) { - 0, 0, - width, height - }, baseline); - - gtk_widget_set_size_request (priv->popup_widget, -1, -1); - - if (priv->popup_fixed_width) - gtk_widget_measure (priv->popup_widget, GTK_ORIENTATION_HORIZONTAL, -1, - &menu_width, NULL, NULL, NULL); - else - gtk_widget_measure (priv->popup_widget, GTK_ORIENTATION_HORIZONTAL, -1, - NULL, &menu_width, NULL, NULL); - - gtk_widget_set_size_request (priv->popup_widget, - MAX (width, menu_width), -1); - - gtk_popover_present (GTK_POPOVER (priv->popup_widget)); -} - -static void -gtk_combo_box_compute_expand (GtkWidget *widget, - gboolean *hexpand, - gboolean *vexpand) -{ - GtkComboBox *combo_box = GTK_COMBO_BOX (widget); - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - GtkWidget *child = priv->child; - - if (child && child != priv->cell_view) - { - *hexpand = gtk_widget_compute_expand (child, GTK_ORIENTATION_HORIZONTAL); - *vexpand = gtk_widget_compute_expand (child, GTK_ORIENTATION_VERTICAL); - } - else - { - *hexpand = FALSE; - *vexpand = FALSE; - } -} - -static void -gtk_combo_box_class_init (GtkComboBoxClass *klass) -{ - GObjectClass *object_class; - GtkWidgetClass *widget_class; - - widget_class = (GtkWidgetClass *)klass; - widget_class->mnemonic_activate = gtk_combo_box_mnemonic_activate; - widget_class->grab_focus = gtk_combo_box_grab_focus; - widget_class->focus = gtk_widget_focus_child; - widget_class->measure = gtk_combo_box_measure; - widget_class->size_allocate = gtk_combo_box_size_allocate; - widget_class->unmap = gtk_combo_box_unmap; - widget_class->compute_expand = gtk_combo_box_compute_expand; - - object_class = (GObjectClass *)klass; - object_class->constructed = gtk_combo_box_constructed; - object_class->dispose = gtk_combo_box_dispose; - object_class->set_property = gtk_combo_box_set_property; - object_class->get_property = gtk_combo_box_get_property; - - klass->activate = gtk_combo_box_activate; - klass->format_entry_text = gtk_combo_box_format_entry_text; - - /* signals */ - /** - * GtkComboBox::activate: - * @widget: the object which received the signal. - * - * Emitted to when the combo box is activated. - * - * The `::activate` signal on `GtkComboBox` is an action signal and - * emitting it causes the combo box to pop up its dropdown. - * - * Since: 4.6 - */ - combo_box_signals[ACTIVATE] = - g_signal_new (I_ ("activate"), - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (GtkComboBoxClass, activate), - NULL, NULL, - NULL, - G_TYPE_NONE, 0); - - gtk_widget_class_set_activate_signal (widget_class, combo_box_signals[ACTIVATE]); - - /** - * GtkComboBox::changed: - * @widget: the object which received the signal - * - * Emitted when the active item is changed. - * - * The can be due to the user selecting a different item from the list, - * or due to a call to [method@Gtk.ComboBox.set_active_iter]. It will - * also be emitted while typing into the entry of a combo box with an entry. - */ - combo_box_signals[CHANGED] = - g_signal_new (I_("changed"), - G_OBJECT_CLASS_TYPE (klass), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkComboBoxClass, changed), - NULL, NULL, - NULL, - G_TYPE_NONE, 0); - - /** - * GtkComboBox::move-active: - * @widget: the object that received the signal - * @scroll_type: a `GtkScrollType` - * - * Emitted to move the active selection. - * - * This is an [keybinding signal](class.SignalAction.html). - */ - combo_box_signals[MOVE_ACTIVE] = - g_signal_new_class_handler (I_("move-active"), - G_OBJECT_CLASS_TYPE (klass), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_CALLBACK (gtk_combo_box_real_move_active), - NULL, NULL, - NULL, - G_TYPE_NONE, 1, - GTK_TYPE_SCROLL_TYPE); - - /** - * GtkComboBox::popup: - * @widget: the object that received the signal - * - * Emitted to popup the combo box list. - * - * This is an [keybinding signal](class.SignalAction.html). - * - * The default binding for this signal is Alt+Down. - */ - combo_box_signals[POPUP] = - g_signal_new_class_handler (I_("popup"), - G_OBJECT_CLASS_TYPE (klass), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_CALLBACK (gtk_combo_box_real_popup), - NULL, NULL, - NULL, - G_TYPE_NONE, 0); - /** - * GtkComboBox::popdown: - * @button: the object which received the signal - * - * Emitted to popdown the combo box list. - * - * This is an [keybinding signal](class.SignalAction.html). - * - * The default bindings for this signal are Alt+Up and Escape. - */ - combo_box_signals[POPDOWN] = - g_signal_new_class_handler (I_("popdown"), - G_OBJECT_CLASS_TYPE (klass), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_CALLBACK (gtk_combo_box_real_popdown), - NULL, NULL, - _gtk_marshal_BOOLEAN__VOID, - G_TYPE_BOOLEAN, 0); - - /** - * GtkComboBox::format-entry-text: - * @combo: the object which received the signal - * @path: the [struct@Gtk.TreePath] string from the combo box's current model - * to format text for - * - * Emitted to allow changing how the text in a combo box's entry is displayed. - * - * See [property@Gtk.ComboBox:has-entry]. - * - * Connect a signal handler which returns an allocated string representing - * @path. That string will then be used to set the text in the combo box's - * entry. The default signal handler uses the text from the - * [property@Gtk.ComboBox:entry-text-column] model column. - * - * Here's an example signal handler which fetches data from the model and - * displays it in the entry. - * ```c - * static char * - * format_entry_text_callback (GtkComboBox *combo, - * const char *path, - * gpointer user_data) - * { - * GtkTreeIter iter; - * GtkTreeModel model; - * double value; - * - * model = gtk_combo_box_get_model (combo); - * - * gtk_tree_model_get_iter_from_string (model, &iter, path); - * gtk_tree_model_get (model, &iter, - * THE_DOUBLE_VALUE_COLUMN, &value, - * -1); - * - * return g_strdup_printf ("%g", value); - * } - * ``` - * - * Returns: (transfer full): a newly allocated string representing @path - * for the current `GtkComboBox` model. - */ - combo_box_signals[FORMAT_ENTRY_TEXT] = - g_signal_new (I_("format-entry-text"), - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkComboBoxClass, format_entry_text), - _gtk_single_string_accumulator, NULL, - _gtk_marshal_STRING__STRING, - G_TYPE_STRING, 1, G_TYPE_STRING); - - /* key bindings */ - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Down, GDK_ALT_MASK, - "popup", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Down, GDK_ALT_MASK, - "popup", - NULL); - - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Up, GDK_ALT_MASK, - "popdown", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Up, GDK_ALT_MASK, - "popdown", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Escape, 0, - "popdown", - NULL); - - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Up, 0, - "move-active", - "(i)", GTK_SCROLL_STEP_UP); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Up, 0, - "move-active", - "(i)", GTK_SCROLL_STEP_UP); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Page_Up, 0, - "move-active", - "(i)", GTK_SCROLL_PAGE_UP); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Page_Up, 0, - "move-active", - "(i)", GTK_SCROLL_PAGE_UP); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Home, 0, - "move-active", - "(i)", GTK_SCROLL_START); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Home, 0, - "move-active", - "(i)", GTK_SCROLL_START); - - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Down, 0, - "move-active", - "(i)", GTK_SCROLL_STEP_DOWN); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Down, 0, - "move-active", - "(i)", GTK_SCROLL_STEP_DOWN); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Page_Down, 0, - "move-active", - "(i)", GTK_SCROLL_PAGE_DOWN); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Page_Down, 0, - "move-active", - "(i)", GTK_SCROLL_PAGE_DOWN); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_End, 0, - "move-active", - "(i)", GTK_SCROLL_END); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_End, 0, - "move-active", - "(i)", GTK_SCROLL_END); - - /* properties */ - g_object_class_override_property (object_class, - PROP_EDITING_CANCELED, - "editing-canceled"); - - /** - * GtkComboBox:model: (attributes org.gtk.Property.get=gtk_combo_box_get_model org.gtk.Property.set=gtk_combo_box_set_model) - * - * The model from which the combo box takes its values. - */ - g_object_class_install_property (object_class, - PROP_MODEL, - g_param_spec_object ("model", NULL, NULL, - GTK_TYPE_TREE_MODEL, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - - /** - * GtkComboBox:active: (attributes org.gtk.Property.get=gtk_combo_box_get_active org.gtk.Property.set=gtk_combo_box_set_active) - * - * The item which is currently active. - * - * If the model is a non-flat treemodel, and the active item is not an - * immediate child of the root of the tree, this property has the value - * `gtk_tree_path_get_indices (path)[0]`, where `path` is the - * [struct@Gtk.TreePath] of the active item. - */ - g_object_class_install_property (object_class, - PROP_ACTIVE, - g_param_spec_int ("active", NULL, NULL, - -1, - G_MAXINT, - -1, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - /** - * GtkComboBox:has-frame: - * - * The `has-frame` property controls whether a frame is drawn around the entry. - */ - g_object_class_install_property (object_class, - PROP_HAS_FRAME, - g_param_spec_boolean ("has-frame", NULL, NULL, - TRUE, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - /** - * GtkComboBox:popup-shown: - * - * Whether the combo boxes dropdown is popped up. - * - * Note that this property is mainly useful, because - * it allows you to connect to notify::popup-shown. - */ - g_object_class_install_property (object_class, - PROP_POPUP_SHOWN, - g_param_spec_boolean ("popup-shown", NULL, NULL, - FALSE, - GTK_PARAM_READABLE)); - - - /** - * GtkComboBox:button-sensitivity: (attributes org.gtk.Property.get=gtk_combo_box_get_button_sensitivity org.gtk.Property.set=gtk_combo_box_set_button_sensitivity) - * - * Whether the dropdown button is sensitive when - * the model is empty. - */ - g_object_class_install_property (object_class, - PROP_BUTTON_SENSITIVITY, - g_param_spec_enum ("button-sensitivity", NULL, NULL, - GTK_TYPE_SENSITIVITY_TYPE, - GTK_SENSITIVITY_AUTO, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - /** - * GtkComboBox:has-entry: (attributes org.gtk.Property.get=gtk_combo_box_get_has_entry) - * - * Whether the combo box has an entry. - */ - g_object_class_install_property (object_class, - PROP_HAS_ENTRY, - g_param_spec_boolean ("has-entry", NULL, NULL, - FALSE, - GTK_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY)); - - /** - * GtkComboBox:entry-text-column: (attributes org.gtk.Property.get=gtk_combo_box_get_entry_text_column org.gtk.Property.set=gtk_combo_box_set_entry_text_column) - * - * The model column to associate with strings from the entry. - * - * This is property only relevant if the combo was created with - * [property@Gtk.ComboBox:has-entry] is %TRUE. - */ - g_object_class_install_property (object_class, - PROP_ENTRY_TEXT_COLUMN, - g_param_spec_int ("entry-text-column", NULL, NULL, - -1, G_MAXINT, -1, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - /** - * GtkComboBox:id-column: (attributes org.gtk.Property.get=gtk_combo_box_get_id_column org.gtk.Property.set=gtk_combo_box_set_id_column) - * - * The model column that provides string IDs for the values - * in the model, if != -1. - */ - g_object_class_install_property (object_class, - PROP_ID_COLUMN, - g_param_spec_int ("id-column", NULL, NULL, - -1, G_MAXINT, -1, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - /** - * GtkComboBox:active-id: (attributes org.gtk.Property.get=gtk_combo_box_get_active_id org.gtk.Property.set=gtk_combo_box_set_active_id) - * - * The value of the ID column of the active row. - */ - g_object_class_install_property (object_class, - PROP_ACTIVE_ID, - g_param_spec_string ("active-id", NULL, NULL, - NULL, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - /** - * GtkComboBox:popup-fixed-width: (attributes org.gtk.Property.get=gtk_combo_box_get_popup_fixed_width org.gtk.Property.set=gtk_combo_box_set_popup_fixed_width) - * - * Whether the popup's width should be a fixed width matching the - * allocated width of the combo box. - */ - g_object_class_install_property (object_class, - PROP_POPUP_FIXED_WIDTH, - g_param_spec_boolean ("popup-fixed-width", NULL, NULL, - TRUE, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - /** - * GtkComboBox:child: (attributes org.gtk.Property.get=gtk_combo_box_get_child org.gtk.Property.set=gtk_combo_box_set_child) - * - * The child widget. - */ - g_object_class_install_property (object_class, - PROP_CHILD, - g_param_spec_object ("child", NULL, NULL, - GTK_TYPE_WIDGET, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/ui/gtkcombobox.ui"); - gtk_widget_class_bind_template_child_internal_private (widget_class, GtkComboBox, box); - gtk_widget_class_bind_template_child_internal_private (widget_class, GtkComboBox, button); - gtk_widget_class_bind_template_child_internal_private (widget_class, GtkComboBox, arrow); - gtk_widget_class_bind_template_child_internal_private (widget_class, GtkComboBox, area); - gtk_widget_class_bind_template_child_internal_private (widget_class, GtkComboBox, popup_widget); - gtk_widget_class_bind_template_callback (widget_class, gtk_combo_box_button_toggled); - gtk_widget_class_bind_template_callback (widget_class, gtk_combo_box_menu_activate); - gtk_widget_class_bind_template_callback (widget_class, gtk_combo_box_menu_key); - gtk_widget_class_bind_template_callback (widget_class, gtk_combo_box_menu_show); - gtk_widget_class_bind_template_callback (widget_class, gtk_combo_box_menu_hide); - - gtk_widget_class_set_css_name (widget_class, I_("combobox")); - gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_COMBO_BOX); -} - -static void -gtk_combo_box_buildable_init (GtkBuildableIface *iface) -{ - parent_buildable_iface = g_type_interface_peek_parent (iface); - iface->add_child = gtk_combo_box_buildable_add_child; - iface->custom_tag_start = gtk_combo_box_buildable_custom_tag_start; - iface->custom_tag_end = gtk_combo_box_buildable_custom_tag_end; - iface->get_internal_child = gtk_combo_box_buildable_get_internal_child; -} - -static void -gtk_combo_box_cell_layout_init (GtkCellLayoutIface *iface) -{ - iface->get_area = gtk_combo_box_cell_layout_get_area; -} - -static void -gtk_combo_box_cell_editable_init (GtkCellEditableIface *iface) -{ - iface->start_editing = gtk_combo_box_start_editing; -} - -static gboolean -gtk_combo_box_row_separator_func (GtkTreeModel *model, - GtkTreeIter *iter, - GtkComboBox *combo_box) -{ - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - - if (priv->row_separator_func) - return priv->row_separator_func (model, iter, priv->row_separator_data); - - return FALSE; -} - -static void -gtk_combo_box_init (GtkComboBox *combo_box) -{ - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - GtkEventController *controller; - GtkEventController **controllers; - guint n_controllers, i; - - priv->active = -1; - priv->active_row = NULL; - - priv->popup_shown = FALSE; - priv->has_frame = TRUE; - priv->is_cell_renderer = FALSE; - priv->editing_canceled = FALSE; - priv->auto_scroll = FALSE; - priv->button_sensitivity = GTK_SENSITIVITY_AUTO; - priv->has_entry = FALSE; - priv->popup_fixed_width = TRUE; - - priv->text_column = -1; - priv->text_renderer = NULL; - priv->id_column = -1; - - g_type_ensure (GTK_TYPE_BUILTIN_ICON); - g_type_ensure (GTK_TYPE_TREE_POPOVER); - gtk_widget_init_template (GTK_WIDGET (combo_box)); - - gtk_widget_remove_css_class (priv->button, "toggle"); - gtk_widget_add_css_class (priv->button, "combo"); - - gtk_tree_popover_set_row_separator_func (GTK_TREE_POPOVER (priv->popup_widget), - (GtkTreeViewRowSeparatorFunc)gtk_combo_box_row_separator_func, - combo_box, NULL); - - controller = gtk_event_controller_scroll_new (GTK_EVENT_CONTROLLER_SCROLL_VERTICAL | - GTK_EVENT_CONTROLLER_SCROLL_DISCRETE); - g_signal_connect (controller, "scroll", - G_CALLBACK (gtk_combo_box_scroll_controller_scroll), - combo_box); - gtk_widget_add_controller (GTK_WIDGET (combo_box), controller); - - controllers = gtk_widget_list_controllers (priv->popup_widget, GTK_PHASE_BUBBLE, &n_controllers); - for (i = 0; i < n_controllers; i ++) - { - controller = controllers[i]; - - if (GTK_IS_SHORTCUT_CONTROLLER (controller)) - { - g_object_ref (controller); - gtk_widget_remove_controller (priv->popup_widget, controller); - gtk_widget_add_controller (priv->popup_widget, controller); - break; - } - } - g_free (controllers); -} - -static void -gtk_combo_box_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - GtkComboBox *combo_box = GTK_COMBO_BOX (object); - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - - switch (prop_id) - { - case PROP_MODEL: - gtk_combo_box_set_model (combo_box, g_value_get_object (value)); - break; - - case PROP_ACTIVE: - gtk_combo_box_set_active (combo_box, g_value_get_int (value)); - break; - - case PROP_HAS_FRAME: - if (priv->has_frame != g_value_get_boolean (value)) - { - priv->has_frame = g_value_get_boolean (value); - if (priv->has_entry) - gtk_entry_set_has_frame (GTK_ENTRY (priv->child), priv->has_frame); - g_object_notify (object, "has-frame"); - } - break; - - case PROP_POPUP_SHOWN: - if (g_value_get_boolean (value)) - gtk_combo_box_popup (combo_box); - else - gtk_combo_box_popdown (combo_box); - break; - - case PROP_BUTTON_SENSITIVITY: - gtk_combo_box_set_button_sensitivity (combo_box, - g_value_get_enum (value)); - break; - - case PROP_POPUP_FIXED_WIDTH: - gtk_combo_box_set_popup_fixed_width (combo_box, - g_value_get_boolean (value)); - break; - - case PROP_EDITING_CANCELED: - if (priv->editing_canceled != g_value_get_boolean (value)) - { - priv->editing_canceled = g_value_get_boolean (value); - g_object_notify (object, "editing-canceled"); - } - break; - - case PROP_HAS_ENTRY: - priv->has_entry = g_value_get_boolean (value); - break; - - case PROP_ENTRY_TEXT_COLUMN: - gtk_combo_box_set_entry_text_column (combo_box, g_value_get_int (value)); - break; - - case PROP_ID_COLUMN: - gtk_combo_box_set_id_column (combo_box, g_value_get_int (value)); - break; - - case PROP_ACTIVE_ID: - gtk_combo_box_set_active_id (combo_box, g_value_get_string (value)); - break; - - case PROP_CHILD: - gtk_combo_box_set_child (combo_box, g_value_get_object (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gtk_combo_box_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GtkComboBox *combo_box = GTK_COMBO_BOX (object); - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - - switch (prop_id) - { - case PROP_MODEL: - g_value_set_object (value, priv->model); - break; - - case PROP_ACTIVE: - g_value_set_int (value, gtk_combo_box_get_active (combo_box)); - break; - - case PROP_HAS_FRAME: - g_value_set_boolean (value, priv->has_frame); - break; - - case PROP_POPUP_SHOWN: - g_value_set_boolean (value, priv->popup_shown); - break; - - case PROP_BUTTON_SENSITIVITY: - g_value_set_enum (value, priv->button_sensitivity); - break; - - case PROP_POPUP_FIXED_WIDTH: - g_value_set_boolean (value, priv->popup_fixed_width); - break; - - case PROP_EDITING_CANCELED: - g_value_set_boolean (value, priv->editing_canceled); - break; - - case PROP_HAS_ENTRY: - g_value_set_boolean (value, priv->has_entry); - break; - - case PROP_ENTRY_TEXT_COLUMN: - g_value_set_int (value, priv->text_column); - break; - - case PROP_ID_COLUMN: - g_value_set_int (value, priv->id_column); - break; - - case PROP_ACTIVE_ID: - g_value_set_string (value, gtk_combo_box_get_active_id (combo_box)); - break; - - case PROP_CHILD: - g_value_set_object (value, gtk_combo_box_get_child (combo_box)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gtk_combo_box_button_toggled (GtkWidget *widget, - gpointer data) -{ - GtkComboBox *combo_box = GTK_COMBO_BOX (data); - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - - if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) - { - if (!priv->popup_in_progress) - gtk_combo_box_popup (combo_box); - } - else - { - gtk_combo_box_popdown (combo_box); - } -} - -static void -gtk_combo_box_create_child (GtkComboBox *combo_box) -{ - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - - if (priv->has_entry) - { - GtkWidget *entry; - - entry = gtk_entry_new (); - gtk_combo_box_set_child (combo_box, entry); - - gtk_widget_add_css_class (GTK_WIDGET (entry), "combo"); - - g_signal_connect (combo_box, "changed", - G_CALLBACK (gtk_combo_box_entry_active_changed), NULL); - } - else - { - priv->cell_view = gtk_cell_view_new_with_context (priv->area, NULL); - gtk_widget_set_hexpand (priv->cell_view, TRUE); - gtk_cell_view_set_fit_model (GTK_CELL_VIEW (priv->cell_view), TRUE); - gtk_cell_view_set_model (GTK_CELL_VIEW (priv->cell_view), priv->model); - gtk_box_insert_child_after (GTK_BOX (gtk_widget_get_parent (priv->arrow)), priv->cell_view, NULL); - priv->child = priv->cell_view; - } -} - -static void -gtk_combo_box_add (GtkComboBox *combo_box, - GtkWidget *widget) -{ - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - - if (priv->box == NULL) - { - gtk_widget_set_parent (widget, GTK_WIDGET (combo_box)); - return; - } - - if (priv->has_entry && !GTK_IS_ENTRY (widget)) - { - g_warning ("Attempting to add a widget with type %s to a GtkComboBox that needs an entry " - "(need an instance of GtkEntry or of a subclass)", - G_OBJECT_TYPE_NAME (widget)); - return; - } - - g_clear_pointer (&priv->cell_view, gtk_widget_unparent); - - gtk_widget_set_hexpand (widget, TRUE); - gtk_box_insert_child_after (GTK_BOX (priv->box), widget, NULL); - - priv->child = widget; - - if (priv->has_entry) - { - g_signal_connect (widget, "changed", - G_CALLBACK (gtk_combo_box_entry_contents_changed), - combo_box); - - gtk_entry_set_has_frame (GTK_ENTRY (widget), priv->has_frame); - } -} - -static void -gtk_combo_box_remove (GtkComboBox *combo_box, - GtkWidget *widget) -{ - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - GtkTreePath *path; - - if (priv->has_entry) - { - if (widget && widget == priv->child) - g_signal_handlers_disconnect_by_func (widget, - gtk_combo_box_entry_contents_changed, - combo_box); - } - - gtk_box_remove (GTK_BOX (priv->box), widget); - - priv->child = NULL; - - if (gtk_widget_in_destruction (GTK_WIDGET (combo_box))) - return; - - gtk_widget_queue_resize (GTK_WIDGET (combo_box)); - - gtk_combo_box_create_child (combo_box); - - if (gtk_tree_row_reference_valid (priv->active_row)) - { - path = gtk_tree_row_reference_get_path (priv->active_row); - gtk_combo_box_set_active_internal (combo_box, path); - gtk_tree_path_free (path); - } - else - { - gtk_combo_box_set_active_internal (combo_box, NULL); - } -} - -static void -gtk_combo_box_menu_show (GtkWidget *menu, - gpointer user_data) -{ - GtkComboBox *combo_box = GTK_COMBO_BOX (user_data); - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - - gtk_combo_box_child_show (menu, user_data); - - priv->popup_in_progress = TRUE; - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button), - TRUE); - priv->popup_in_progress = FALSE; -} - -static void -gtk_combo_box_menu_hide (GtkWidget *menu, - gpointer user_data) -{ - GtkComboBox *combo_box = GTK_COMBO_BOX (user_data); - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - - gtk_combo_box_child_hide (menu,user_data); - - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button), FALSE); -} - -static gboolean -cell_layout_is_sensitive (GtkCellLayout *layout) -{ - GList *cells, *list; - gboolean sensitive; - - cells = gtk_cell_layout_get_cells (layout); - - sensitive = FALSE; - for (list = cells; list; list = list->next) - { - g_object_get (list->data, "sensitive", &sensitive, NULL); - - if (sensitive) - break; - } - g_list_free (cells); - - return sensitive; -} - -static gboolean -tree_column_row_is_sensitive (GtkComboBox *combo_box, - GtkTreeIter *iter) -{ - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - - if (priv->row_separator_func) - { - if (priv->row_separator_func (priv->model, iter, - priv->row_separator_data)) - return FALSE; - } - - gtk_cell_area_apply_attributes (priv->area, priv->model, iter, FALSE, FALSE); - return cell_layout_is_sensitive (GTK_CELL_LAYOUT (priv->area)); -} - -static void -gtk_combo_box_menu_popup (GtkComboBox *combo_box) -{ - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); -#if 0 - int active_item; - GtkWidget *active; - int width, min_width, nat_width; -#endif - - gtk_tree_popover_open_submenu (GTK_TREE_POPOVER (priv->popup_widget), "main"); - gtk_popover_popup (GTK_POPOVER (priv->popup_widget)); - -#if 0 - active_item = -1; - if (gtk_tree_row_reference_valid (priv->active_row)) - { - GtkTreePath *path; - - path = gtk_tree_row_reference_get_path (priv->active_row); - active_item = gtk_tree_path_get_indices (path)[0]; - gtk_tree_path_free (path); - } - - /* FIXME handle nested menus better */ - //gtk_tree_popover_set_active (GTK_TREE_POPOVER (priv->popup_widget), active_item); - - width = gtk_widget_get_width (GTK_WIDGET (combo_box)); - gtk_widget_set_size_request (priv->popup_widget, -1, -1); - gtk_widget_measure (priv->popup_widget, GTK_ORIENTATION_HORIZONTAL, -1, - &min_width, &nat_width, NULL, NULL); - - if (priv->popup_fixed_width) - width = MAX (width, min_width); - else - width = MAX (width, nat_width); - - gtk_widget_set_size_request (priv->popup_widget, width, -1); - - g_signal_handlers_disconnect_by_func (priv->popup_widget, - gtk_menu_update_scroll_offset, - NULL); - - if (priv->cell_view == NULL) - { - g_object_set (priv->popup_widget, - "anchor-hints", (GDK_ANCHOR_FLIP_Y | - GDK_ANCHOR_SLIDE | - GDK_ANCHOR_RESIZE), - "rect-anchor-dx", 0, - NULL); - - gtk_menu_popup_at_widget (GTK_MENU (priv->popup_widget), - gtk_bin_get_child (GTK_BIN (combo_box)), - GDK_GRAVITY_SOUTH_WEST, - GDK_GRAVITY_NORTH_WEST, - NULL); - } - else - { - int rect_anchor_dy = -2; - GList *i; - GtkWidget *child; - - /* FIXME handle nested menus better */ - active = gtk_menu_get_active (GTK_MENU (priv->popup_widget)); - - if (!(active && gtk_widget_get_visible (active))) - { - GList *children; - children = gtk_menu_shell_get_items (GTK_MENU_SHELL (priv->popup_widget)); - for (i = children; i && !active; i = i->next) - { - child = i->data; - - if (child && gtk_widget_get_visible (child)) - active = child; - } - g_list_free (children); - } - - if (active) - { - int child_height; - GList *children; - children = gtk_menu_shell_get_items (GTK_MENU_SHELL (priv->popup_widget)); - for (i = children; i && i->data != active; i = i->next) - { - child = i->data; - - if (child && gtk_widget_get_visible (child)) - { - gtk_widget_measure (child, GTK_ORIENTATION_VERTICAL, -1, - &child_height, NULL, NULL, NULL); - rect_anchor_dy -= child_height; - } - } - g_list_free (children); - - gtk_widget_measure (active, GTK_ORIENTATION_VERTICAL, -1, - &child_height, NULL, NULL, NULL); - rect_anchor_dy -= child_height / 2; - } - - g_object_set (priv->popup_widget, - "anchor-hints", (GDK_ANCHOR_SLIDE | - GDK_ANCHOR_RESIZE), - "rect-anchor-dy", rect_anchor_dy, - NULL); - - g_signal_connect (priv->popup_widget, - "popped-up", - G_CALLBACK (gtk_menu_update_scroll_offset), - NULL); - - gtk_menu_popup_at_widget (GTK_MENU (priv->popup_widget), - GTK_WIDGET (combo_box), - GDK_GRAVITY_WEST, - GDK_GRAVITY_NORTH_WEST, - NULL); - } -#endif -} - -/** - * gtk_combo_box_popup: - * @combo_box: a `GtkComboBox` - * - * Pops up the menu or dropdown list of @combo_box. - * - * This function is mostly intended for use by accessibility technologies; - * applications should have little use for it. - * - * Before calling this, @combo_box must be mapped, or nothing will happen. - */ -void -gtk_combo_box_popup (GtkComboBox *combo_box) -{ - g_return_if_fail (GTK_IS_COMBO_BOX (combo_box)); - - if (gtk_widget_get_mapped (GTK_WIDGET (combo_box))) - g_signal_emit (combo_box, combo_box_signals[POPUP], 0); -} - -/** - * gtk_combo_box_popup_for_device: - * @combo_box: a `GtkComboBox` - * @device: a `GdkDevice` - * - * Pops up the menu of @combo_box. - * - * Note that currently this does not do anything with the device, as it was - * previously only used for list-mode combo boxes, and those were removed - * in GTK 4. However, it is retained in case similar functionality is added - * back later. - */ -void -gtk_combo_box_popup_for_device (GtkComboBox *combo_box, - GdkDevice *device) -{ - /* As above, this currently does not do anything useful, and nothing with the - * passed-in device. But the bits that are not blatantly obsolete are kept. */ - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - - g_return_if_fail (GTK_IS_COMBO_BOX (combo_box)); - g_return_if_fail (GDK_IS_DEVICE (device)); - - if (!gtk_widget_get_realized (GTK_WIDGET (combo_box))) - return; - - if (gtk_widget_get_mapped (priv->popup_widget)) - return; - - gtk_combo_box_menu_popup (combo_box); -} - -static void -gtk_combo_box_real_popup (GtkComboBox *combo_box) -{ - gtk_combo_box_menu_popup (combo_box); -} - -static gboolean -gtk_combo_box_real_popdown (GtkComboBox *combo_box) -{ - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - - if (priv->popup_shown) - { - gtk_combo_box_popdown (combo_box); - return TRUE; - } - - return FALSE; -} - -/** - * gtk_combo_box_popdown: - * @combo_box: a `GtkComboBox` - * - * Hides the menu or dropdown list of @combo_box. - * - * This function is mostly intended for use by accessibility technologies; - * applications should have little use for it. - */ -void -gtk_combo_box_popdown (GtkComboBox *combo_box) -{ - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - - g_return_if_fail (GTK_IS_COMBO_BOX (combo_box)); - - gtk_popover_popdown (GTK_POPOVER (priv->popup_widget)); -} - -static void -gtk_combo_box_unset_model (GtkComboBox *combo_box) -{ - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - - if (priv->model) - { - g_signal_handlers_disconnect_by_func (priv->model, - gtk_combo_box_model_row_inserted, - combo_box); - g_signal_handlers_disconnect_by_func (priv->model, - gtk_combo_box_model_row_deleted, - combo_box); - g_signal_handlers_disconnect_by_func (priv->model, - gtk_combo_box_model_rows_reordered, - combo_box); - g_signal_handlers_disconnect_by_func (priv->model, - gtk_combo_box_model_row_changed, - combo_box); - - g_object_unref (priv->model); - priv->model = NULL; - } - - if (priv->active_row) - { - gtk_tree_row_reference_free (priv->active_row); - priv->active_row = NULL; - } - - if (priv->cell_view) - gtk_cell_view_set_model (GTK_CELL_VIEW (priv->cell_view), NULL); -} - -static void -gtk_combo_box_child_show (GtkWidget *widget, - GtkComboBox *combo_box) -{ - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - - priv->popup_shown = TRUE; - g_object_notify (G_OBJECT (combo_box), "popup-shown"); -} - -static void -gtk_combo_box_child_hide (GtkWidget *widget, - GtkComboBox *combo_box) -{ - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - - priv->popup_shown = FALSE; - g_object_notify (G_OBJECT (combo_box), "popup-shown"); -} - -typedef struct { - GtkComboBox *combo; - GtkTreePath *path; - GtkTreeIter iter; - gboolean found; - gboolean set; -} SearchData; - -static gboolean -tree_next_func (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data) -{ - SearchData *search_data = (SearchData *)data; - - if (search_data->found) - { - if (!tree_column_row_is_sensitive (search_data->combo, iter)) - return FALSE; - - search_data->set = TRUE; - search_data->iter = *iter; - - return TRUE; - } - - if (gtk_tree_path_compare (path, search_data->path) == 0) - search_data->found = TRUE; - - return FALSE; -} - -static gboolean -tree_next (GtkComboBox *combo, - GtkTreeModel *model, - GtkTreeIter *iter, - GtkTreeIter *next) -{ - SearchData search_data; - - search_data.combo = combo; - search_data.path = gtk_tree_model_get_path (model, iter); - search_data.found = FALSE; - search_data.set = FALSE; - - gtk_tree_model_foreach (model, tree_next_func, &search_data); - - *next = search_data.iter; - - gtk_tree_path_free (search_data.path); - - return search_data.set; -} - -static gboolean -tree_prev_func (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data) -{ - SearchData *search_data = (SearchData *)data; - - if (gtk_tree_path_compare (path, search_data->path) == 0) - { - search_data->found = TRUE; - return TRUE; - } - - if (!tree_column_row_is_sensitive (search_data->combo, iter)) - return FALSE; - - search_data->set = TRUE; - search_data->iter = *iter; - - return FALSE; -} - -static gboolean -tree_prev (GtkComboBox *combo, - GtkTreeModel *model, - GtkTreeIter *iter, - GtkTreeIter *prev) -{ - SearchData search_data; - - search_data.combo = combo; - search_data.path = gtk_tree_model_get_path (model, iter); - search_data.found = FALSE; - search_data.set = FALSE; - - gtk_tree_model_foreach (model, tree_prev_func, &search_data); - - *prev = search_data.iter; - - gtk_tree_path_free (search_data.path); - - return search_data.set; -} - -static gboolean -tree_last_func (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data) -{ - SearchData *search_data = (SearchData *)data; - - if (!tree_column_row_is_sensitive (search_data->combo, iter)) - return FALSE; - - search_data->set = TRUE; - search_data->iter = *iter; - - return FALSE; -} - -static gboolean -tree_last (GtkComboBox *combo, - GtkTreeModel *model, - GtkTreeIter *last) -{ - SearchData search_data; - - search_data.combo = combo; - search_data.set = FALSE; - - gtk_tree_model_foreach (model, tree_last_func, &search_data); - - *last = search_data.iter; - - return search_data.set; -} - - -static gboolean -tree_first_func (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data) -{ - SearchData *search_data = (SearchData *)data; - - if (!tree_column_row_is_sensitive (search_data->combo, iter)) - return FALSE; - - search_data->set = TRUE; - search_data->iter = *iter; - - return TRUE; -} - -static gboolean -tree_first (GtkComboBox *combo, - GtkTreeModel *model, - GtkTreeIter *first) -{ - SearchData search_data; - - search_data.combo = combo; - search_data.set = FALSE; - - gtk_tree_model_foreach (model, tree_first_func, &search_data); - - *first = search_data.iter; - - return search_data.set; -} - -static gboolean -gtk_combo_box_scroll_controller_scroll (GtkEventControllerScroll *scroll, - double dx, - double dy, - GtkComboBox *combo_box) -{ - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - gboolean found = FALSE; - GtkTreeIter iter; - GtkTreeIter new_iter; - - if (!gtk_combo_box_get_active_iter (combo_box, &iter)) - return GDK_EVENT_PROPAGATE; - - if (dy < 0) - found = tree_prev (combo_box, priv->model, &iter, &new_iter); - else if (dy > 0) - found = tree_next (combo_box, priv->model, &iter, &new_iter); - - if (found) - gtk_combo_box_set_active_iter (combo_box, &new_iter); - - return found; - -} - -/* callbacks */ -static void -gtk_combo_box_menu_activate (GtkWidget *menu, - const char *path, - GtkComboBox *combo_box) -{ - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - GtkTreeIter iter; - - if (gtk_tree_model_get_iter_from_string (priv->model, &iter, path)) - gtk_combo_box_set_active_iter (combo_box, &iter); - - g_object_set (combo_box, - "editing-canceled", FALSE, - NULL); -} - - -static void -gtk_combo_box_update_sensitivity (GtkComboBox *combo_box) -{ - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - GtkTreeIter iter; - gboolean sensitive = TRUE; /* fool code checkers */ - - if (!priv->button) - return; - - switch (priv->button_sensitivity) - { - case GTK_SENSITIVITY_ON: - sensitive = TRUE; - break; - case GTK_SENSITIVITY_OFF: - sensitive = FALSE; - break; - case GTK_SENSITIVITY_AUTO: - sensitive = priv->model && - gtk_tree_model_get_iter_first (priv->model, &iter); - break; - default: - g_assert_not_reached (); - break; - } - - gtk_widget_set_sensitive (priv->button, sensitive); -} - -static void -gtk_combo_box_model_row_inserted (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer user_data) -{ - GtkComboBox *combo_box = GTK_COMBO_BOX (user_data); - - gtk_combo_box_update_sensitivity (combo_box); -} - -static void -gtk_combo_box_model_row_deleted (GtkTreeModel *model, - GtkTreePath *path, - gpointer user_data) -{ - GtkComboBox *combo_box = GTK_COMBO_BOX (user_data); - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - - if (!gtk_tree_row_reference_valid (priv->active_row)) - { - if (priv->cell_view) - gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (priv->cell_view), NULL); - g_signal_emit (combo_box, combo_box_signals[CHANGED], 0); - } - - gtk_combo_box_update_sensitivity (combo_box); -} - -static void -gtk_combo_box_model_rows_reordered (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - int *new_order, - gpointer user_data) -{ - gtk_tree_row_reference_reordered (G_OBJECT (user_data), path, iter, new_order); -} - -static void -gtk_combo_box_model_row_changed (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer user_data) -{ - GtkComboBox *combo_box = GTK_COMBO_BOX (user_data); - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - GtkTreePath *active_path; - - /* FIXME this belongs to GtkCellView */ - if (gtk_tree_row_reference_valid (priv->active_row)) - { - active_path = gtk_tree_row_reference_get_path (priv->active_row); - if (gtk_tree_path_compare (path, active_path) == 0 && - priv->cell_view) - gtk_widget_queue_resize (GTK_WIDGET (priv->cell_view)); - gtk_tree_path_free (active_path); - } -} - -static gboolean -gtk_combo_box_menu_key (GtkEventControllerKey *key, - guint keyval, - guint keycode, - GdkModifierType modifiers, - GtkComboBox *combo_box) -{ - gtk_event_controller_key_forward (key, GTK_WIDGET (combo_box)); - - return TRUE; -} - -/* - * GtkCellLayout implementation - */ -static GtkCellArea * -gtk_combo_box_cell_layout_get_area (GtkCellLayout *cell_layout) -{ - GtkComboBox *combo_box = GTK_COMBO_BOX (cell_layout); - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - - return priv->area; -} - -/* - * public API - */ - -/** - * gtk_combo_box_new: - * - * Creates a new empty `GtkComboBox`. - * - * Returns: A new `GtkComboBox` - */ -GtkWidget * -gtk_combo_box_new (void) -{ - return g_object_new (GTK_TYPE_COMBO_BOX, NULL); -} - -/** - * gtk_combo_box_new_with_entry: - * - * Creates a new empty `GtkComboBox` with an entry. - * - * In order to use a combo box with entry, you need to tell it - * which column of the model contains the text for the entry - * by calling [method@Gtk.ComboBox.set_entry_text_column]. - * - * Returns: A new `GtkComboBox` - */ -GtkWidget * -gtk_combo_box_new_with_entry (void) -{ - return g_object_new (GTK_TYPE_COMBO_BOX, "has-entry", TRUE, NULL); -} - -/** - * gtk_combo_box_new_with_model: - * @model: a `GtkTreeModel` - * - * Creates a new `GtkComboBox` with a model. - * - * Returns: A new `GtkComboBox` - */ -GtkWidget * -gtk_combo_box_new_with_model (GtkTreeModel *model) -{ - GtkComboBox *combo_box; - - g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL); - - combo_box = g_object_new (GTK_TYPE_COMBO_BOX, "model", model, NULL); - - return GTK_WIDGET (combo_box); -} - -/** - * gtk_combo_box_new_with_model_and_entry: - * @model: A `GtkTreeModel` - * - * Creates a new empty `GtkComboBox` with an entry and a model. - * - * See also [ctor@Gtk.ComboBox.new_with_entry]. - * - * Returns: A new `GtkComboBox` - */ -GtkWidget * -gtk_combo_box_new_with_model_and_entry (GtkTreeModel *model) -{ - return g_object_new (GTK_TYPE_COMBO_BOX, - "has-entry", TRUE, - "model", model, - NULL); -} - -/** - * gtk_combo_box_get_active: - * @combo_box: A `GtkComboBox` - * - * Returns the index of the currently active item. - * - * If the model is a non-flat treemodel, and the active item is not - * an immediate child of the root of the tree, this function returns - * `gtk_tree_path_get_indices (path)[0]`, where `path` is the - * [struct@Gtk.TreePath] of the active item. - * - * Returns: An integer which is the index of the currently active item, - * or -1 if there’s no active item - */ -int -gtk_combo_box_get_active (GtkComboBox *combo_box) -{ - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - int result; - - g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), 0); - - if (gtk_tree_row_reference_valid (priv->active_row)) - { - GtkTreePath *path; - - path = gtk_tree_row_reference_get_path (priv->active_row); - result = gtk_tree_path_get_indices (path)[0]; - gtk_tree_path_free (path); - } - else - result = -1; - - return result; -} - -/** - * gtk_combo_box_set_active: (attributes org.gtk.Method.set_property=active) - * @combo_box: a `GtkComboBox` - * @index_: An index in the model passed during construction, - * or -1 to have no active item - * - * Sets the active item of @combo_box to be the item at @index. - */ -void -gtk_combo_box_set_active (GtkComboBox *combo_box, - int index_) -{ - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - GtkTreePath *path = NULL; - - g_return_if_fail (GTK_IS_COMBO_BOX (combo_box)); - g_return_if_fail (index_ >= -1); - - if (priv->model == NULL) - { - /* Save index, in case the model is set after the index */ - priv->active = index_; - if (index_ != -1) - return; - } - - if (index_ != -1) - path = gtk_tree_path_new_from_indices (index_, -1); - - gtk_combo_box_set_active_internal (combo_box, path); - - if (path) - gtk_tree_path_free (path); -} - -static void -gtk_combo_box_set_active_internal (GtkComboBox *combo_box, - GtkTreePath *path) -{ - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - GtkTreePath *active_path; - int path_cmp; - - /* Remember whether the initially active row is valid. */ - gboolean is_valid_row_reference = gtk_tree_row_reference_valid (priv->active_row); - - if (path && is_valid_row_reference) - { - active_path = gtk_tree_row_reference_get_path (priv->active_row); - path_cmp = gtk_tree_path_compare (path, active_path); - gtk_tree_path_free (active_path); - if (path_cmp == 0) - return; - } - - if (priv->active_row) - { - gtk_tree_row_reference_free (priv->active_row); - priv->active_row = NULL; - } - - if (!path) - { - gtk_tree_popover_set_active (GTK_TREE_POPOVER (priv->popup_widget), -1); - - if (priv->cell_view) - gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (priv->cell_view), NULL); - - /* - * Do not emit a "changed" signal when an already invalid selection was - * now set to invalid. - */ - if (!is_valid_row_reference) - return; - } - else - { - priv->active_row = - gtk_tree_row_reference_new (priv->model, path); - - gtk_tree_popover_set_active (GTK_TREE_POPOVER (priv->popup_widget), - gtk_tree_path_get_indices (path)[0]); - - if (priv->cell_view) - gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (priv->cell_view), path); - } - - g_signal_emit (combo_box, combo_box_signals[CHANGED], 0); - g_object_notify (G_OBJECT (combo_box), "active"); - if (priv->id_column >= 0) - g_object_notify (G_OBJECT (combo_box), "active-id"); -} - - -/** - * gtk_combo_box_get_active_iter: - * @combo_box: A `GtkComboBox` - * @iter: (out): A `GtkTreeIter` - * - * Sets @iter to point to the currently active item. - * - * If no item is active, @iter is left unchanged. - * - * Returns: %TRUE if @iter was set, %FALSE otherwise - */ -gboolean -gtk_combo_box_get_active_iter (GtkComboBox *combo_box, - GtkTreeIter *iter) -{ - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - GtkTreePath *path; - gboolean result; - - g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE); - - if (!gtk_tree_row_reference_valid (priv->active_row)) - return FALSE; - - path = gtk_tree_row_reference_get_path (priv->active_row); - result = gtk_tree_model_get_iter (priv->model, iter, path); - gtk_tree_path_free (path); - - return result; -} - -/** - * gtk_combo_box_set_active_iter: - * @combo_box: A `GtkComboBox` - * @iter: (nullable): The `GtkTreeIter` - * - * Sets the current active item to be the one referenced by @iter. - * - * If @iter is %NULL, the active item is unset. - */ -void -gtk_combo_box_set_active_iter (GtkComboBox *combo_box, - GtkTreeIter *iter) -{ - GtkTreePath *path = NULL; - - g_return_if_fail (GTK_IS_COMBO_BOX (combo_box)); - - if (iter) - path = gtk_tree_model_get_path (gtk_combo_box_get_model (combo_box), iter); - - gtk_combo_box_set_active_internal (combo_box, path); - gtk_tree_path_free (path); -} - -/** - * gtk_combo_box_set_model: (attributes org.gtk.Method.set_property=model) - * @combo_box: A `GtkComboBox` - * @model: (nullable): A `GtkTreeModel` - * - * Sets the model used by @combo_box to be @model. - * - * Will unset a previously set model (if applicable). If model is %NULL, - * then it will unset the model. - * - * Note that this function does not clear the cell renderers, you have to - * call [method@Gtk.CellLayout.clear] yourself if you need to set up different - * cell renderers for the new model. - */ -void -gtk_combo_box_set_model (GtkComboBox *combo_box, - GtkTreeModel *model) -{ - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - - g_return_if_fail (GTK_IS_COMBO_BOX (combo_box)); - g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model)); - - if (model == priv->model) - return; - - gtk_combo_box_unset_model (combo_box); - - if (model == NULL) - goto out; - - priv->model = model; - g_object_ref (priv->model); - - g_signal_connect (priv->model, "row-inserted", - G_CALLBACK (gtk_combo_box_model_row_inserted), - combo_box); - g_signal_connect (priv->model, "row-deleted", - G_CALLBACK (gtk_combo_box_model_row_deleted), - combo_box); - g_signal_connect (priv->model, "rows-reordered", - G_CALLBACK (gtk_combo_box_model_rows_reordered), - combo_box); - g_signal_connect (priv->model, "row-changed", - G_CALLBACK (gtk_combo_box_model_row_changed), - combo_box); - - gtk_tree_popover_set_model (GTK_TREE_POPOVER (priv->popup_widget), priv->model); - - if (priv->cell_view) - gtk_cell_view_set_model (GTK_CELL_VIEW (priv->cell_view), - priv->model); - - if (priv->active != -1) - { - /* If an index was set in advance, apply it now */ - gtk_combo_box_set_active (combo_box, priv->active); - priv->active = -1; - } - -out: - gtk_combo_box_update_sensitivity (combo_box); - - g_object_notify (G_OBJECT (combo_box), "model"); -} - -/** - * gtk_combo_box_get_model: (attributes org.gtk.Method.get_property=model) - * @combo_box: A `GtkComboBox` - * - * Returns the `GtkTreeModel` of @combo_box. - * - * Returns: (nullable) (transfer none): A `GtkTreeModel` which was passed - * during construction. - */ -GtkTreeModel * -gtk_combo_box_get_model (GtkComboBox *combo_box) -{ - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - - g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL); - - return priv->model; -} - -static void -gtk_combo_box_real_move_active (GtkComboBox *combo_box, - GtkScrollType scroll) -{ - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - GtkTreeIter iter; - GtkTreeIter new_iter; - gboolean active_iter; - gboolean found; - - if (!priv->model) - { - gtk_widget_error_bell (GTK_WIDGET (combo_box)); - return; - } - - active_iter = gtk_combo_box_get_active_iter (combo_box, &iter); - - switch (scroll) - { - case GTK_SCROLL_STEP_BACKWARD: - case GTK_SCROLL_STEP_UP: - case GTK_SCROLL_STEP_LEFT: - if (active_iter) - { - found = tree_prev (combo_box, priv->model, - &iter, &new_iter); - break; - } - G_GNUC_FALLTHROUGH; - - case GTK_SCROLL_PAGE_FORWARD: - case GTK_SCROLL_PAGE_DOWN: - case GTK_SCROLL_PAGE_RIGHT: - case GTK_SCROLL_END: - found = tree_last (combo_box, priv->model, &new_iter); - break; - - case GTK_SCROLL_STEP_FORWARD: - case GTK_SCROLL_STEP_DOWN: - case GTK_SCROLL_STEP_RIGHT: - if (active_iter) - { - found = tree_next (combo_box, priv->model, - &iter, &new_iter); - break; - } - G_GNUC_FALLTHROUGH; - - case GTK_SCROLL_PAGE_BACKWARD: - case GTK_SCROLL_PAGE_UP: - case GTK_SCROLL_PAGE_LEFT: - case GTK_SCROLL_START: - found = tree_first (combo_box, priv->model, &new_iter); - break; - - case GTK_SCROLL_NONE: - case GTK_SCROLL_JUMP: - default: - return; - } - - if (found && active_iter) - { - GtkTreePath *old_path; - GtkTreePath *new_path; - - old_path = gtk_tree_model_get_path (priv->model, &iter); - new_path = gtk_tree_model_get_path (priv->model, &new_iter); - - if (gtk_tree_path_compare (old_path, new_path) == 0) - found = FALSE; - - gtk_tree_path_free (old_path); - gtk_tree_path_free (new_path); - } - - if (found) - { - gtk_combo_box_set_active_iter (combo_box, &new_iter); - } - else - { - gtk_widget_error_bell (GTK_WIDGET (combo_box)); - } -} - -static gboolean -gtk_combo_box_mnemonic_activate (GtkWidget *widget, - gboolean group_cycling) -{ - GtkComboBox *combo_box = GTK_COMBO_BOX (widget); - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - - if (priv->has_entry) - { - if (priv->child) - gtk_widget_grab_focus (priv->child); - } - else - gtk_widget_mnemonic_activate (priv->button, group_cycling); - - return TRUE; -} - -static gboolean -gtk_combo_box_grab_focus (GtkWidget *widget) -{ - GtkComboBox *combo_box = GTK_COMBO_BOX (widget); - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - - if (priv->has_entry) - { - if (priv->child) - return gtk_widget_grab_focus (priv->child); - else - return FALSE; - } - else - return gtk_widget_grab_focus (priv->button); -} - -static void -gtk_combo_box_unmap (GtkWidget *widget) -{ - gtk_combo_box_popdown (GTK_COMBO_BOX (widget)); - - GTK_WIDGET_CLASS (gtk_combo_box_parent_class)->unmap (widget); -} - -static void -gtk_combo_box_entry_contents_changed (GtkEntry *entry, - gpointer user_data) -{ - GtkComboBox *combo_box = GTK_COMBO_BOX (user_data); - - /* - * Fixes regression reported in bug #574059. The old functionality relied on - * bug #572478. As a bugfix, we now emit the "changed" signal ourselves - * when the selection was already set to -1. - */ - if (gtk_combo_box_get_active(combo_box) == -1) - g_signal_emit_by_name (combo_box, "changed"); - else - gtk_combo_box_set_active (combo_box, -1); -} - -static void -gtk_combo_box_entry_active_changed (GtkComboBox *combo_box, - gpointer user_data) -{ - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - GtkTreeModel *model; - GtkTreeIter iter; - - if (gtk_combo_box_get_active_iter (combo_box, &iter)) - { - GtkEntry *entry = GTK_ENTRY (priv->child); - - if (entry) - { - GtkTreePath *path; - char *path_str; - char *text = NULL; - - model = gtk_combo_box_get_model (combo_box); - path = gtk_tree_model_get_path (model, &iter); - path_str = gtk_tree_path_to_string (path); - - g_signal_handlers_block_by_func (entry, - gtk_combo_box_entry_contents_changed, - combo_box); - - - g_signal_emit (combo_box, combo_box_signals[FORMAT_ENTRY_TEXT], 0, - path_str, &text); - - gtk_editable_set_text (GTK_EDITABLE (entry), text); - - g_signal_handlers_unblock_by_func (entry, - gtk_combo_box_entry_contents_changed, - combo_box); - - gtk_tree_path_free (path); - g_free (text); - g_free (path_str); - } - } -} - -static char * -gtk_combo_box_format_entry_text (GtkComboBox *combo_box, - const char *path) -{ - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - GtkTreeModel *model; - GtkTreeIter iter; - char *text = NULL; - - if (priv->text_column >= 0) - { - model = gtk_combo_box_get_model (combo_box); - gtk_tree_model_get_iter_from_string (model, &iter, path); - - gtk_tree_model_get (model, &iter, - priv->text_column, &text, - -1); - } - - return text; -} - -static void -gtk_combo_box_constructed (GObject *object) -{ - GtkComboBox *combo_box = GTK_COMBO_BOX (object); - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - - G_OBJECT_CLASS (gtk_combo_box_parent_class)->constructed (object); - - gtk_combo_box_create_child (combo_box); - - if (priv->has_entry) - { - priv->text_renderer = gtk_cell_renderer_text_new (); - gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), - priv->text_renderer, TRUE); - - gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), -1); - } -} - -static void -gtk_combo_box_dispose (GObject* object) -{ - GtkComboBox *combo_box = GTK_COMBO_BOX (object); - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - - if (priv->popup_idle_id > 0) - { - g_source_remove (priv->popup_idle_id); - priv->popup_idle_id = 0; - } - - if (priv->box) - { - /* destroy things (unparent will kill the latest ref from us) - * last unref on button will destroy the arrow - */ - gtk_widget_unparent (priv->box); - priv->box = NULL; - priv->button = NULL; - priv->arrow = NULL; - priv->child = NULL; - priv->cell_view = NULL; - } - - if (priv->row_separator_destroy) - priv->row_separator_destroy (priv->row_separator_data); - - priv->row_separator_func = NULL; - priv->row_separator_data = NULL; - priv->row_separator_destroy = NULL; - - if (priv->popup_widget) - { - /* Stop menu destruction triggering toggle on a now-invalid button */ - g_signal_handlers_disconnect_by_func (priv->popup_widget, - gtk_combo_box_menu_hide, - combo_box); - g_clear_pointer (&priv->popup_widget, gtk_widget_unparent); - } - - gtk_combo_box_unset_model (combo_box); - - G_OBJECT_CLASS (gtk_combo_box_parent_class)->dispose (object); -} - -static gboolean -gtk_cell_editable_key_pressed (GtkEventControllerKey *key, - guint keyval, - guint keycode, - GdkModifierType modifiers, - GtkComboBox *combo_box) -{ - if (keyval == GDK_KEY_Escape) - { - g_object_set (combo_box, - "editing-canceled", TRUE, - NULL); - gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box)); - gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box)); - - return TRUE; - } - else if (keyval == GDK_KEY_Return || - keyval == GDK_KEY_ISO_Enter || - keyval == GDK_KEY_KP_Enter) - { - gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box)); - gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box)); - - return TRUE; - } - - return FALSE; -} - -static void -gtk_combo_box_start_editing (GtkCellEditable *cell_editable, - GdkEvent *event) -{ - GtkComboBox *combo_box = GTK_COMBO_BOX (cell_editable); - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - GtkEventController *controller; - - priv->is_cell_renderer = TRUE; - - controller = gtk_event_controller_key_new (); - g_signal_connect_object (controller, "key-pressed", - G_CALLBACK (gtk_cell_editable_key_pressed), - cell_editable, 0); - - if (priv->cell_view) - { - gtk_widget_add_controller (priv->button, controller); - gtk_widget_grab_focus (priv->button); - } - else - { - gtk_widget_add_controller (priv->child, controller); - - gtk_widget_grab_focus (priv->child); - gtk_widget_set_can_focus (priv->button, FALSE); - } -} - -/** - * gtk_combo_box_set_popup_fixed_width: (attributes org.gtk.Method.set_property=popup-fixed-width) - * @combo_box: a `GtkComboBox` - * @fixed: whether to use a fixed popup width - * - * Specifies whether the popup’s width should be a fixed width. - * - * If @fixed is %TRUE, the popup's width is set to match the - * allocated width of the combo box. - */ -void -gtk_combo_box_set_popup_fixed_width (GtkComboBox *combo_box, - gboolean fixed) -{ - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - - g_return_if_fail (GTK_IS_COMBO_BOX (combo_box)); - - if (priv->popup_fixed_width != fixed) - { - priv->popup_fixed_width = fixed; - - g_object_notify (G_OBJECT (combo_box), "popup-fixed-width"); - } -} - -/** - * gtk_combo_box_get_popup_fixed_width: (attributes org.gtk.Method.get_property=popup-fixed-width) - * @combo_box: a `GtkComboBox` - * - * Gets whether the popup uses a fixed width. - * - * Returns: %TRUE if the popup uses a fixed width - */ -gboolean -gtk_combo_box_get_popup_fixed_width (GtkComboBox *combo_box) -{ - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - - g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE); - - return priv->popup_fixed_width; -} - -/** - * gtk_combo_box_get_row_separator_func: (skip) - * @combo_box: a `GtkComboBox` - * - * Returns the current row separator function. - * - * Returns: (nullable): the current row separator function. - */ -GtkTreeViewRowSeparatorFunc -gtk_combo_box_get_row_separator_func (GtkComboBox *combo_box) -{ - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - - g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL); - - return priv->row_separator_func; -} - -/** - * gtk_combo_box_set_row_separator_func: - * @combo_box: a `GtkComboBox` - * @func: (nullable): a `GtkTreeViewRowSeparatorFunc` - * @data: (nullable): user data to pass to @func - * @destroy: (nullable): destroy notifier for @data - * - * Sets the row separator function, which is used to determine - * whether a row should be drawn as a separator. - * - * If the row separator function is %NULL, no separators are drawn. - * This is the default value. - */ -void -gtk_combo_box_set_row_separator_func (GtkComboBox *combo_box, - GtkTreeViewRowSeparatorFunc func, - gpointer data, - GDestroyNotify destroy) -{ - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - - g_return_if_fail (GTK_IS_COMBO_BOX (combo_box)); - - if (priv->row_separator_destroy) - priv->row_separator_destroy (priv->row_separator_data); - - priv->row_separator_func = func; - priv->row_separator_data = data; - priv->row_separator_destroy = destroy; - - gtk_tree_popover_set_row_separator_func (GTK_TREE_POPOVER (priv->popup_widget), - (GtkTreeViewRowSeparatorFunc)gtk_combo_box_row_separator_func, - combo_box, NULL); - - gtk_widget_queue_draw (GTK_WIDGET (combo_box)); -} - -/** - * gtk_combo_box_set_button_sensitivity: (attributes org.gtk.Method.set_property=button-sensitivity) - * @combo_box: a `GtkComboBox` - * @sensitivity: specify the sensitivity of the dropdown button - * - * Sets whether the dropdown button of the combo box should update - * its sensitivity depending on the model contents. - */ -void -gtk_combo_box_set_button_sensitivity (GtkComboBox *combo_box, - GtkSensitivityType sensitivity) -{ - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - - g_return_if_fail (GTK_IS_COMBO_BOX (combo_box)); - - if (priv->button_sensitivity != sensitivity) - { - priv->button_sensitivity = sensitivity; - gtk_combo_box_update_sensitivity (combo_box); - - g_object_notify (G_OBJECT (combo_box), "button-sensitivity"); - } -} - -/** - * gtk_combo_box_get_button_sensitivity: (attributes org.gtk.Method.get_property=button-sensitivity) - * @combo_box: a `GtkComboBox` - * - * Returns whether the combo box sets the dropdown button - * sensitive or not when there are no items in the model. - * - * Returns: %GTK_SENSITIVITY_ON if the dropdown button - * is sensitive when the model is empty, %GTK_SENSITIVITY_OFF - * if the button is always insensitive or %GTK_SENSITIVITY_AUTO - * if it is only sensitive as long as the model has one item to - * be selected. - */ -GtkSensitivityType -gtk_combo_box_get_button_sensitivity (GtkComboBox *combo_box) -{ - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE); - - return priv->button_sensitivity; -} - - -/** - * gtk_combo_box_get_has_entry: (attributes org.gtk.Method.get_property=has-entry) - * @combo_box: a `GtkComboBox` - * - * Returns whether the combo box has an entry. - * - * Returns: whether there is an entry in @combo_box. - */ -gboolean -gtk_combo_box_get_has_entry (GtkComboBox *combo_box) -{ - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE); - - return priv->has_entry; -} - -/** - * gtk_combo_box_set_entry_text_column: - * @combo_box: A `GtkComboBox` - * @text_column: A column in @model to get the strings from for - * the internal entry - * - * Sets the model column which @combo_box should use to get strings - * from to be @text_column. - * - * For this column no separate - * [class@Gtk.CellRenderer] is needed. - * - * The column @text_column in the model of @combo_box must be of - * type %G_TYPE_STRING. - * - * This is only relevant if @combo_box has been created with - * [property@Gtk.ComboBox:has-entry] as %TRUE. - */ -void -gtk_combo_box_set_entry_text_column (GtkComboBox *combo_box, - int text_column) -{ - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - - g_return_if_fail (GTK_IS_COMBO_BOX (combo_box)); - g_return_if_fail (text_column >= 0); - g_return_if_fail (priv->model == NULL || text_column < gtk_tree_model_get_n_columns (priv->model)); - - if (priv->text_column != text_column) - { - priv->text_column = text_column; - - if (priv->text_renderer != NULL) - gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), - priv->text_renderer, - "text", text_column, - NULL); - - g_object_notify (G_OBJECT (combo_box), "entry-text-column"); - } -} - -/** - * gtk_combo_box_get_entry_text_column: - * @combo_box: A `GtkComboBox` - * - * Returns the column which @combo_box is using to get the strings - * from to display in the internal entry. - * - * Returns: A column in the data source model of @combo_box. - */ -int -gtk_combo_box_get_entry_text_column (GtkComboBox *combo_box) -{ - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - - g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), 0); - - return priv->text_column; -} - -static void -gtk_combo_box_buildable_add_child (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const char *type) -{ - if (GTK_IS_CELL_RENDERER (child)) - _gtk_cell_layout_buildable_add_child (buildable, builder, child, type); - else if (GTK_IS_WIDGET (child)) - gtk_combo_box_set_child (GTK_COMBO_BOX (buildable), GTK_WIDGET (child)); - else - parent_buildable_iface->add_child (buildable, builder, child, type); -} - -static gboolean -gtk_combo_box_buildable_custom_tag_start (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const char *tagname, - GtkBuildableParser *parser, - gpointer *data) -{ - if (parent_buildable_iface->custom_tag_start (buildable, builder, child, - tagname, parser, data)) - return TRUE; - - return _gtk_cell_layout_buildable_custom_tag_start (buildable, builder, child, - tagname, parser, data); -} - -static void -gtk_combo_box_buildable_custom_tag_end (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const char *tagname, - gpointer data) -{ - if (!_gtk_cell_layout_buildable_custom_tag_end (buildable, builder, child, tagname, data)) - parent_buildable_iface->custom_tag_end (buildable, builder, child, tagname, data); -} - -static GObject * -gtk_combo_box_buildable_get_internal_child (GtkBuildable *buildable, - GtkBuilder *builder, - const char *childname) -{ - GtkComboBox *combo_box = GTK_COMBO_BOX (buildable); - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - - if (priv->has_entry && strcmp (childname, "entry") == 0) - return G_OBJECT (priv->child); - - return parent_buildable_iface->get_internal_child (buildable, builder, childname); -} - -/** - * gtk_combo_box_set_id_column: (attributes org.gtk.Method.set_property=id-column) - * @combo_box: A `GtkComboBox` - * @id_column: A column in @model to get string IDs for values from - * - * Sets the model column which @combo_box should use to get string IDs - * for values from. - * - * The column @id_column in the model of @combo_box must be of type - * %G_TYPE_STRING. - */ -void -gtk_combo_box_set_id_column (GtkComboBox *combo_box, - int id_column) -{ - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - - g_return_if_fail (GTK_IS_COMBO_BOX (combo_box)); - - if (id_column != priv->id_column) - { - g_return_if_fail (id_column >= 0); - g_return_if_fail (priv->model == NULL || id_column < gtk_tree_model_get_n_columns (priv->model)); - - priv->id_column = id_column; - - g_object_notify (G_OBJECT (combo_box), "id-column"); - g_object_notify (G_OBJECT (combo_box), "active-id"); - } -} - -/** - * gtk_combo_box_get_id_column: (attributes org.gtk.Method.get_property=id-column) - * @combo_box: A `GtkComboBox` - * - * Returns the column which @combo_box is using to get string IDs - * for values from. - * - * Returns: A column in the data source model of @combo_box. - */ -int -gtk_combo_box_get_id_column (GtkComboBox *combo_box) -{ - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - - g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), 0); - - return priv->id_column; -} - -/** - * gtk_combo_box_get_active_id: (attributes org.gtk.Method.get_property=active-id) - * @combo_box: a `GtkComboBox` - * - * Returns the ID of the active row of @combo_box. - * - * This value is taken from the active row and the column specified - * by the [property@Gtk.ComboBox:id-column] property of @combo_box - * (see [method@Gtk.ComboBox.set_id_column]). - * - * The returned value is an interned string which means that you can - * compare the pointer by value to other interned strings and that you - * must not free it. - * - * If the [property@Gtk.ComboBox:id-column] property of @combo_box is - * not set, or if no row is active, or if the active row has a %NULL - * ID value, then %NULL is returned. - * - * Returns: (nullable): the ID of the active row - */ -const char * -gtk_combo_box_get_active_id (GtkComboBox *combo_box) -{ - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - GtkTreeModel *model; - GtkTreeIter iter; - int column; - - g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL); - - column = priv->id_column; - - if (column < 0) - return NULL; - - model = gtk_combo_box_get_model (combo_box); - g_return_val_if_fail (gtk_tree_model_get_column_type (model, column) == - G_TYPE_STRING, NULL); - - if (gtk_combo_box_get_active_iter (combo_box, &iter)) - { - const char *interned; - char *id; - - gtk_tree_model_get (model, &iter, column, &id, -1); - interned = g_intern_string (id); - g_free (id); - - return interned; - } - - return NULL; -} - -/** - * gtk_combo_box_set_active_id: (attributes org.gtk.Method.set_property=active-id) - * @combo_box: a `GtkComboBox` - * @active_id: (nullable): the ID of the row to select - * - * Changes the active row of @combo_box to the one that has an ID equal to - * @active_id. - * - * If @active_id is %NULL, the active row is unset. Rows having - * a %NULL ID string cannot be made active by this function. - * - * If the [property@Gtk.ComboBox:id-column] property of @combo_box is - * unset or if no row has the given ID then the function does nothing - * and returns %FALSE. - * - * Returns: %TRUE if a row with a matching ID was found. If a %NULL - * @active_id was given to unset the active row, the function - * always returns %TRUE. - */ -gboolean -gtk_combo_box_set_active_id (GtkComboBox *combo_box, - const char *active_id) -{ - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - GtkTreeModel *model; - GtkTreeIter iter; - gboolean match = FALSE; - int column; - - g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE); - - if (active_id == NULL) - { - gtk_combo_box_set_active (combo_box, -1); - return TRUE; /* active row was successfully unset */ - } - - column = priv->id_column; - - if (column < 0) - return FALSE; - - model = gtk_combo_box_get_model (combo_box); - g_return_val_if_fail (gtk_tree_model_get_column_type (model, column) == - G_TYPE_STRING, FALSE); - - if (gtk_tree_model_get_iter_first (model, &iter)) - do { - char *id; - - gtk_tree_model_get (model, &iter, column, &id, -1); - if (id != NULL) - match = strcmp (id, active_id) == 0; - g_free (id); - - if (match) - { - gtk_combo_box_set_active_iter (combo_box, &iter); - break; - } - } while (gtk_tree_model_iter_next (model, &iter)); - - g_object_notify (G_OBJECT (combo_box), "active-id"); - - return match; -} - -GtkWidget * -gtk_combo_box_get_popup (GtkComboBox *combo_box) -{ - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - - return priv->popup_widget; -} - -/** - * gtk_combo_box_set_child: (attributes org.gtk.Method.set_property=child) - * @combo_box: a `GtkComboBox` - * @child: (nullable): the child widget - * - * Sets the child widget of @combo_box. - */ -void -gtk_combo_box_set_child (GtkComboBox *combo_box, - GtkWidget *child) -{ - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - - g_return_if_fail (GTK_IS_COMBO_BOX (combo_box)); - g_return_if_fail (child == NULL || GTK_IS_WIDGET (child)); - - if (priv->child) - gtk_combo_box_remove (combo_box, priv->child); - - if (child) - gtk_combo_box_add (combo_box, child); - - g_object_notify (G_OBJECT (combo_box), "child"); -} - -/** - * gtk_combo_box_get_child: (attributes org.gtk.Method.get_property=child) - * @combo_box: a `GtkComboBox` - * - * Gets the child widget of @combo_box. - * - * Returns: (nullable) (transfer none): the child widget of @combo_box - */ -GtkWidget * -gtk_combo_box_get_child (GtkComboBox *combo_box) -{ - GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); - - g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL); - - return priv->child; -} - diff --git a/gtk/gtkcombobox.h b/gtk/gtkcombobox.h deleted file mode 100644 index 3740801fa9..0000000000 --- a/gtk/gtkcombobox.h +++ /dev/null @@ -1,161 +0,0 @@ -/* gtkcombobox.h - * Copyright (C) 2002, 2003 Kristian Rietveld - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#ifndef __GTK_COMBO_BOX_H__ -#define __GTK_COMBO_BOX_H__ - -#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) -#error "Only can be included directly." -#endif - -#include -#include -#include - -G_BEGIN_DECLS - -#define GTK_TYPE_COMBO_BOX (gtk_combo_box_get_type ()) -#define GTK_COMBO_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_COMBO_BOX, GtkComboBox)) -#define GTK_COMBO_BOX_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST ((vtable), GTK_TYPE_COMBO_BOX, GtkComboBoxClass)) -#define GTK_IS_COMBO_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_COMBO_BOX)) -#define GTK_IS_COMBO_BOX_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), GTK_TYPE_COMBO_BOX)) -#define GTK_COMBO_BOX_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), GTK_TYPE_COMBO_BOX, GtkComboBoxClass)) - -typedef struct _GtkComboBox GtkComboBox; -typedef struct _GtkComboBoxClass GtkComboBoxClass; - -struct _GtkComboBox -{ - GtkWidget parent_instance; -}; - -/** - * GtkComboBoxClass: - * @parent_class: The parent class. - * @changed: Signal is emitted when the active item is changed. - * @format_entry_text: Signal which allows you to change how the text - * displayed in a combo box’s entry is displayed. - */ -struct _GtkComboBoxClass -{ - GtkWidgetClass parent_class; - - /*< public >*/ - - /* signals */ - void (* changed) (GtkComboBox *combo_box); - char *(* format_entry_text) (GtkComboBox *combo_box, - const char *path); - void (* activate) (GtkComboBox *combo_box); - - /*< private >*/ - - gpointer padding[7]; -}; - - -/* construction */ -GDK_AVAILABLE_IN_ALL -GType gtk_combo_box_get_type (void) G_GNUC_CONST; -GDK_AVAILABLE_IN_ALL -GtkWidget *gtk_combo_box_new (void); -GDK_AVAILABLE_IN_ALL -GtkWidget *gtk_combo_box_new_with_entry (void); -GDK_AVAILABLE_IN_ALL -GtkWidget *gtk_combo_box_new_with_model (GtkTreeModel *model); -GDK_AVAILABLE_IN_ALL -GtkWidget *gtk_combo_box_new_with_model_and_entry (GtkTreeModel *model); - -/* get/set active item */ -GDK_AVAILABLE_IN_ALL -int gtk_combo_box_get_active (GtkComboBox *combo_box); -GDK_AVAILABLE_IN_ALL -void gtk_combo_box_set_active (GtkComboBox *combo_box, - int index_); -GDK_AVAILABLE_IN_ALL -gboolean gtk_combo_box_get_active_iter (GtkComboBox *combo_box, - GtkTreeIter *iter); -GDK_AVAILABLE_IN_ALL -void gtk_combo_box_set_active_iter (GtkComboBox *combo_box, - GtkTreeIter *iter); - -/* getters and setters */ -GDK_AVAILABLE_IN_ALL -void gtk_combo_box_set_model (GtkComboBox *combo_box, - GtkTreeModel *model); -GDK_AVAILABLE_IN_ALL -GtkTreeModel *gtk_combo_box_get_model (GtkComboBox *combo_box); - -GDK_AVAILABLE_IN_ALL -GtkTreeViewRowSeparatorFunc gtk_combo_box_get_row_separator_func (GtkComboBox *combo_box); -GDK_AVAILABLE_IN_ALL -void gtk_combo_box_set_row_separator_func (GtkComboBox *combo_box, - GtkTreeViewRowSeparatorFunc func, - gpointer data, - GDestroyNotify destroy); - -GDK_AVAILABLE_IN_ALL -void gtk_combo_box_set_button_sensitivity (GtkComboBox *combo_box, - GtkSensitivityType sensitivity); -GDK_AVAILABLE_IN_ALL -GtkSensitivityType gtk_combo_box_get_button_sensitivity (GtkComboBox *combo_box); - -GDK_AVAILABLE_IN_ALL -gboolean gtk_combo_box_get_has_entry (GtkComboBox *combo_box); -GDK_AVAILABLE_IN_ALL -void gtk_combo_box_set_entry_text_column (GtkComboBox *combo_box, - int text_column); -GDK_AVAILABLE_IN_ALL -int gtk_combo_box_get_entry_text_column (GtkComboBox *combo_box); - -GDK_AVAILABLE_IN_ALL -void gtk_combo_box_set_popup_fixed_width (GtkComboBox *combo_box, - gboolean fixed); -GDK_AVAILABLE_IN_ALL -gboolean gtk_combo_box_get_popup_fixed_width (GtkComboBox *combo_box); - -/* programmatic control */ -GDK_AVAILABLE_IN_ALL -void gtk_combo_box_popup (GtkComboBox *combo_box); -GDK_AVAILABLE_IN_ALL -void gtk_combo_box_popup_for_device (GtkComboBox *combo_box, - GdkDevice *device); -GDK_AVAILABLE_IN_ALL -void gtk_combo_box_popdown (GtkComboBox *combo_box); - -GDK_AVAILABLE_IN_ALL -int gtk_combo_box_get_id_column (GtkComboBox *combo_box); -GDK_AVAILABLE_IN_ALL -void gtk_combo_box_set_id_column (GtkComboBox *combo_box, - int id_column); -GDK_AVAILABLE_IN_ALL -const char * gtk_combo_box_get_active_id (GtkComboBox *combo_box); -GDK_AVAILABLE_IN_ALL -gboolean gtk_combo_box_set_active_id (GtkComboBox *combo_box, - const char *active_id); - -GDK_AVAILABLE_IN_ALL -void gtk_combo_box_set_child (GtkComboBox *combo_box, - GtkWidget *child); -GDK_AVAILABLE_IN_ALL -GtkWidget * gtk_combo_box_get_child (GtkComboBox *combo_box); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkComboBox, g_object_unref) - -G_END_DECLS - -#endif /* __GTK_COMBO_BOX_H__ */ diff --git a/gtk/gtkcomboboxprivate.h b/gtk/gtkcomboboxprivate.h deleted file mode 100644 index 6af67e06c6..0000000000 --- a/gtk/gtkcomboboxprivate.h +++ /dev/null @@ -1,29 +0,0 @@ -/* GTK - The GIMP Toolkit - * Copyright (C) 2014 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see . - */ - -#ifndef __GTK_COMBO_BOX_PRIVATE_H__ -#define __GTK_COMBO_BOX_PRIVATE_H__ - -#include "gtkcombobox.h" - -G_BEGIN_DECLS - -GtkWidget *gtk_combo_box_get_popup (GtkComboBox *combo_box); - -G_END_DECLS - -#endif /* __GTK_COMBO_BOX_PRIVATE_H__ */ diff --git a/gtk/gtkcomboboxtext.c b/gtk/gtkcomboboxtext.c deleted file mode 100644 index 9415544359..0000000000 --- a/gtk/gtkcomboboxtext.c +++ /dev/null @@ -1,619 +0,0 @@ -/* GTK - The GIMP Toolkit - * - * Copyright (C) 2010 Christian Dywan - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see . - */ - -#include "config.h" - -#include "gtkcomboboxtext.h" -#include "gtkcombobox.h" -#include "gtkcellrenderertext.h" -#include "gtkcelllayout.h" -#include "gtkbuildable.h" -#include "gtkbuilderprivate.h" -#include "gtkliststore.h" - -#include - -/** - * GtkComboBoxText: - * - * A `GtkComboBoxText` is a simple variant of `GtkComboBox` for text-only - * use cases. - * - * ![An example GtkComboBoxText](combo-box-text.png) - * - * `GtkComboBoxText` hides the model-view complexity of `GtkComboBox`. - * - * To create a `GtkComboBoxText`, use [ctor@Gtk.ComboBoxText.new] or - * [ctor@Gtk.ComboBoxText.new_with_entry]. - * - * You can add items to a `GtkComboBoxText` with - * [method@Gtk.ComboBoxText.append_text], - * [method@Gtk.ComboBoxText.insert_text] or - * [method@Gtk.ComboBoxText.prepend_text] and remove options with - * [method@Gtk.ComboBoxText.remove]. - * - * If the `GtkComboBoxText` contains an entry (via the - * [property@Gtk.ComboBox:has-entry] property), its contents can be retrieved - * using [method@Gtk.ComboBoxText.get_active_text]. - * - * You should not call [method@Gtk.ComboBox.set_model] or attempt to pack more - * cells into this combo box via its [iface@Gtk.CellLayout] interface. - * - * # GtkComboBoxText as GtkBuildable - * - * The `GtkComboBoxText` implementation of the `GtkBuildable` interface supports - * adding items directly using the element and specifying - * elements for each item. Each element can specify the “id” - * corresponding to the appended text and also supports the regular - * translation attributes “translatable”, “context” and “comments”. - * - * Here is a UI definition fragment specifying `GtkComboBoxText` items: - * ```xml - * - * - * Factory - * Home - * Subway - * - * - * ``` - * - * # CSS nodes - * - * ``` - * combobox - * ╰── box.linked - * ├── entry.combo - * ├── button.combo - * ╰── window.popup - * ``` - * - * `GtkComboBoxText` has a single CSS node with name combobox. It adds - * the style class .combo to the main CSS nodes of its entry and button - * children, and the .linked class to the node of its internal box. - */ - -typedef struct _GtkComboBoxTextClass GtkComboBoxTextClass; - -struct _GtkComboBoxText -{ - GtkComboBox parent_instance; -}; - -struct _GtkComboBoxTextClass -{ - GtkComboBoxClass parent_class; -}; - - -static void gtk_combo_box_text_buildable_interface_init (GtkBuildableIface *iface); -static gboolean gtk_combo_box_text_buildable_custom_tag_start (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const char *tagname, - GtkBuildableParser *parser, - gpointer *data); - -static void gtk_combo_box_text_buildable_custom_finished (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const char *tagname, - gpointer user_data); - - -static GtkBuildableIface *buildable_parent_iface = NULL; - -G_DEFINE_TYPE_WITH_CODE (GtkComboBoxText, gtk_combo_box_text, GTK_TYPE_COMBO_BOX, - G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, - gtk_combo_box_text_buildable_interface_init)); - -static void -gtk_combo_box_text_constructed (GObject *object) -{ - const int text_column = 0; - - G_OBJECT_CLASS (gtk_combo_box_text_parent_class)->constructed (object); - - gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX (object), text_column); - gtk_combo_box_set_id_column (GTK_COMBO_BOX (object), 1); - - if (!gtk_combo_box_get_has_entry (GTK_COMBO_BOX (object))) - { - GtkCellRenderer *cell; - - cell = gtk_cell_renderer_text_new (); - gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (object), cell, TRUE); - gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (object), cell, - "text", text_column, - NULL); - } -} - -static void -gtk_combo_box_text_init (GtkComboBoxText *combo_box) -{ - GtkListStore *store; - - store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING); - gtk_combo_box_set_model (GTK_COMBO_BOX (combo_box), GTK_TREE_MODEL (store)); - g_object_unref (store); -} - -static void -gtk_combo_box_text_class_init (GtkComboBoxTextClass *klass) -{ - GObjectClass *object_class; - - object_class = (GObjectClass *)klass; - object_class->constructed = gtk_combo_box_text_constructed; -} - -static void -gtk_combo_box_text_buildable_interface_init (GtkBuildableIface *iface) -{ - buildable_parent_iface = g_type_interface_peek_parent (iface); - - iface->custom_tag_start = gtk_combo_box_text_buildable_custom_tag_start; - iface->custom_finished = gtk_combo_box_text_buildable_custom_finished; -} - -typedef struct { - GtkBuilder *builder; - GObject *object; - const char *domain; - char *id; - - GString *string; - - char *context; - guint translatable : 1; - - guint is_text : 1; -} ItemParserData; - -static void -item_start_element (GtkBuildableParseContext *context, - const char *element_name, - const char **names, - const char **values, - gpointer user_data, - GError **error) -{ - ItemParserData *data = (ItemParserData*)user_data; - - if (strcmp (element_name, "items") == 0) - { - if (!_gtk_builder_check_parent (data->builder, context, "object", error)) - return; - - if (!g_markup_collect_attributes (element_name, names, values, error, - G_MARKUP_COLLECT_INVALID, NULL, NULL, - G_MARKUP_COLLECT_INVALID)) - _gtk_builder_prefix_error (data->builder, context, error); - } - else if (strcmp (element_name, "item") == 0) - { - const char *id = NULL; - gboolean translatable = FALSE; - const char *msg_context = NULL; - - if (!_gtk_builder_check_parent (data->builder, context, "items", error)) - return; - - if (!g_markup_collect_attributes (element_name, names, values, error, - G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "id", &id, - G_MARKUP_COLLECT_BOOLEAN|G_MARKUP_COLLECT_OPTIONAL, "translatable", &translatable, - G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "comments", NULL, - G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "context", &msg_context, - G_MARKUP_COLLECT_INVALID)) - { - _gtk_builder_prefix_error (data->builder, context, error); - return; - } - - data->is_text = TRUE; - data->translatable = translatable; - data->context = g_strdup (msg_context); - data->id = g_strdup (id); - } - else - { - _gtk_builder_error_unhandled_tag (data->builder, context, - "GtkComboBoxText", element_name, - error); - } -} - -static void -item_text (GtkBuildableParseContext *context, - const char *text, - gsize text_len, - gpointer user_data, - GError **error) -{ - ItemParserData *data = (ItemParserData*)user_data; - - if (data->is_text) - g_string_append_len (data->string, text, text_len); -} - -static void -item_end_element (GtkBuildableParseContext *context, - const char *element_name, - gpointer user_data, - GError **error) -{ - ItemParserData *data = (ItemParserData*)user_data; - - /* Append the translated strings */ - if (data->string->len) - { - if (data->translatable) - { - const char *translated; - - translated = _gtk_builder_parser_translate (data->domain, - data->context, - data->string->str); - g_string_assign (data->string, translated); - } - - gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (data->object), data->id, data->string->str); - } - - data->translatable = FALSE; - g_string_set_size (data->string, 0); - g_clear_pointer (&data->context, g_free); - g_clear_pointer (&data->id, g_free); - data->is_text = FALSE; -} - -static const GtkBuildableParser item_parser = - { - item_start_element, - item_end_element, - item_text - }; - -static gboolean -gtk_combo_box_text_buildable_custom_tag_start (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const char *tagname, - GtkBuildableParser *parser, - gpointer *parser_data) -{ - if (buildable_parent_iface->custom_tag_start (buildable, builder, child, - tagname, parser, parser_data)) - return TRUE; - - if (strcmp (tagname, "items") == 0) - { - ItemParserData *data; - - data = g_slice_new0 (ItemParserData); - data->builder = g_object_ref (builder); - data->object = (GObject *) g_object_ref (buildable); - data->domain = gtk_builder_get_translation_domain (builder); - data->string = g_string_new (""); - - *parser = item_parser; - *parser_data = data; - - return TRUE; - } - - return FALSE; -} - -static void -gtk_combo_box_text_buildable_custom_finished (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const char *tagname, - gpointer user_data) -{ - ItemParserData *data; - - buildable_parent_iface->custom_finished (buildable, builder, child, - tagname, user_data); - - if (strcmp (tagname, "items") == 0) - { - data = (ItemParserData*)user_data; - - g_object_unref (data->object); - g_object_unref (data->builder); - g_string_free (data->string, TRUE); - g_slice_free (ItemParserData, data); - } -} - -/** - * gtk_combo_box_text_new: - * - * Creates a new `GtkComboBoxText`. - * - * Returns: A new `GtkComboBoxText` - */ -GtkWidget * -gtk_combo_box_text_new (void) -{ - return g_object_new (GTK_TYPE_COMBO_BOX_TEXT, - NULL); -} - -/** - * gtk_combo_box_text_new_with_entry: - * - * Creates a new `GtkComboBoxText` with an entry. - * - * Returns: a new `GtkComboBoxText` - */ -GtkWidget * -gtk_combo_box_text_new_with_entry (void) -{ - return g_object_new (GTK_TYPE_COMBO_BOX_TEXT, - "has-entry", TRUE, - NULL); -} - -/** - * gtk_combo_box_text_append_text: - * @combo_box: A `GtkComboBoxText` - * @text: A string - * - * Appends @text to the list of strings stored in @combo_box. - * - * This is the same as calling [method@Gtk.ComboBoxText.insert_text] - * with a position of -1. - */ -void -gtk_combo_box_text_append_text (GtkComboBoxText *combo_box, - const char *text) -{ - gtk_combo_box_text_insert (combo_box, -1, NULL, text); -} - -/** - * gtk_combo_box_text_prepend_text: - * @combo_box: A `GtkComboBox` - * @text: A string - * - * Prepends @text to the list of strings stored in @combo_box. - * - * This is the same as calling [method@Gtk.ComboBoxText.insert_text] - * with a position of 0. - */ -void -gtk_combo_box_text_prepend_text (GtkComboBoxText *combo_box, - const char *text) -{ - gtk_combo_box_text_insert (combo_box, 0, NULL, text); -} - -/** - * gtk_combo_box_text_insert_text: - * @combo_box: A `GtkComboBoxText` - * @position: An index to insert @text - * @text: A string - * - * Inserts @text at @position in the list of strings stored in @combo_box. - * - * If @position is negative then @text is appended. - * - * This is the same as calling [method@Gtk.ComboBoxText.insert] - * with a %NULL ID string. - */ -void -gtk_combo_box_text_insert_text (GtkComboBoxText *combo_box, - int position, - const char *text) -{ - gtk_combo_box_text_insert (combo_box, position, NULL, text); -} - -/** - * gtk_combo_box_text_append: - * @combo_box: A `GtkComboBoxText` - * @id: (nullable): a string ID for this value - * @text: A string - * - * Appends @text to the list of strings stored in @combo_box. - * - * If @id is non-%NULL then it is used as the ID of the row. - * - * This is the same as calling [method@Gtk.ComboBoxText.insert] - * with a position of -1. - */ -void -gtk_combo_box_text_append (GtkComboBoxText *combo_box, - const char *id, - const char *text) -{ - gtk_combo_box_text_insert (combo_box, -1, id, text); -} - -/** - * gtk_combo_box_text_prepend: - * @combo_box: A `GtkComboBox` - * @id: (nullable): a string ID for this value - * @text: a string - * - * Prepends @text to the list of strings stored in @combo_box. - * - * If @id is non-%NULL then it is used as the ID of the row. - * - * This is the same as calling [method@Gtk.ComboBoxText.insert] - * with a position of 0. - */ -void -gtk_combo_box_text_prepend (GtkComboBoxText *combo_box, - const char *id, - const char *text) -{ - gtk_combo_box_text_insert (combo_box, 0, id, text); -} - - -/** - * gtk_combo_box_text_insert: - * @combo_box: A `GtkComboBoxText` - * @position: An index to insert @text - * @id: (nullable): a string ID for this value - * @text: A string to display - * - * Inserts @text at @position in the list of strings stored in @combo_box. - * - * If @id is non-%NULL then it is used as the ID of the row. - * See [property@Gtk.ComboBox:id-column]. - * - * If @position is negative then @text is appended. - */ -void -gtk_combo_box_text_insert (GtkComboBoxText *combo_box, - int position, - const char *id, - const char *text) -{ - GtkListStore *store; - GtkTreeIter iter; - int text_column; - - g_return_if_fail (GTK_IS_COMBO_BOX_TEXT (combo_box)); - g_return_if_fail (text != NULL); - - store = GTK_LIST_STORE (gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box))); - g_return_if_fail (GTK_IS_LIST_STORE (store)); - - text_column = gtk_combo_box_get_entry_text_column (GTK_COMBO_BOX (combo_box)); - - if (gtk_combo_box_get_has_entry (GTK_COMBO_BOX (combo_box))) - g_return_if_fail (text_column >= 0); - else if (text_column < 0) - text_column = 0; - - g_return_if_fail (gtk_tree_model_get_column_type (GTK_TREE_MODEL (store), text_column) == G_TYPE_STRING); - - if (position < 0) - gtk_list_store_append (store, &iter); - else - gtk_list_store_insert (store, &iter, position); - - gtk_list_store_set (store, &iter, text_column, text, -1); - - if (id != NULL) - { - int id_column; - - id_column = gtk_combo_box_get_id_column (GTK_COMBO_BOX (combo_box)); - g_return_if_fail (id_column >= 0); - g_return_if_fail (gtk_tree_model_get_column_type (GTK_TREE_MODEL (store), id_column) == G_TYPE_STRING); - - gtk_list_store_set (store, &iter, id_column, id, -1); - } -} - -/** - * gtk_combo_box_text_remove: - * @combo_box: A `GtkComboBox` - * @position: Index of the item to remove - * - * Removes the string at @position from @combo_box. - */ -void -gtk_combo_box_text_remove (GtkComboBoxText *combo_box, - int position) -{ - GtkTreeModel *model; - GtkListStore *store; - GtkTreeIter iter; - - g_return_if_fail (GTK_IS_COMBO_BOX_TEXT (combo_box)); - g_return_if_fail (position >= 0); - - model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box)); - store = GTK_LIST_STORE (model); - g_return_if_fail (GTK_IS_LIST_STORE (store)); - - if (gtk_tree_model_iter_nth_child (model, &iter, NULL, position)) - gtk_list_store_remove (store, &iter); -} - -/** - * gtk_combo_box_text_remove_all: - * @combo_box: A `GtkComboBoxText` - * - * Removes all the text entries from the combo box. - */ -void -gtk_combo_box_text_remove_all (GtkComboBoxText *combo_box) -{ - GtkListStore *store; - - g_return_if_fail (GTK_IS_COMBO_BOX_TEXT (combo_box)); - - store = GTK_LIST_STORE (gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box))); - gtk_list_store_clear (store); -} - -/** - * gtk_combo_box_text_get_active_text: - * @combo_box: A `GtkComboBoxText` - * - * Returns the currently active string in @combo_box. - * - * If no row is currently selected, %NULL is returned. - * If @combo_box contains an entry, this function will - * return its contents (which will not necessarily - * be an item from the list). - * - * Returns: (nullable) (transfer full): a newly allocated - * string containing the currently active text. - * Must be freed with g_free(). - */ -char * -gtk_combo_box_text_get_active_text (GtkComboBoxText *combo_box) -{ - GtkTreeIter iter; - char *text = NULL; - - g_return_val_if_fail (GTK_IS_COMBO_BOX_TEXT (combo_box), NULL); - - if (gtk_combo_box_get_has_entry (GTK_COMBO_BOX (combo_box))) - { - GtkWidget *entry; - - entry = gtk_combo_box_get_child (GTK_COMBO_BOX (combo_box)); - text = g_strdup (gtk_editable_get_text (GTK_EDITABLE (entry))); - } - else if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo_box), &iter)) - { - GtkTreeModel *model; - int text_column; - - model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box)); - g_return_val_if_fail (GTK_IS_LIST_STORE (model), NULL); - text_column = gtk_combo_box_get_entry_text_column (GTK_COMBO_BOX (combo_box)); - g_return_val_if_fail (text_column >= 0, NULL); - g_return_val_if_fail (gtk_tree_model_get_column_type (model, text_column) == G_TYPE_STRING, NULL); - gtk_tree_model_get (model, &iter, text_column, &text, -1); - } - - return text; -} diff --git a/gtk/gtkcomboboxtext.h b/gtk/gtkcomboboxtext.h deleted file mode 100644 index d90cc9d19b..0000000000 --- a/gtk/gtkcomboboxtext.h +++ /dev/null @@ -1,78 +0,0 @@ -/* GTK - The GIMP Toolkit - * - * Copyright (C) 2010 Christian Dywan - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see . - */ - -#ifndef __GTK_COMBO_BOX_TEXT_H__ -#define __GTK_COMBO_BOX_TEXT_H__ - -#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) -#error "Only can be included directly." -#endif - -#include - -G_BEGIN_DECLS - -#define GTK_TYPE_COMBO_BOX_TEXT (gtk_combo_box_text_get_type ()) -#define GTK_COMBO_BOX_TEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_COMBO_BOX_TEXT, GtkComboBoxText)) -#define GTK_IS_COMBO_BOX_TEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_COMBO_BOX_TEXT)) - -typedef struct _GtkComboBoxText GtkComboBoxText; - -GDK_AVAILABLE_IN_ALL -GType gtk_combo_box_text_get_type (void) G_GNUC_CONST; -GDK_AVAILABLE_IN_ALL -GtkWidget* gtk_combo_box_text_new (void); -GDK_AVAILABLE_IN_ALL -GtkWidget* gtk_combo_box_text_new_with_entry (void); -GDK_AVAILABLE_IN_ALL -void gtk_combo_box_text_append_text (GtkComboBoxText *combo_box, - const char *text); -GDK_AVAILABLE_IN_ALL -void gtk_combo_box_text_insert_text (GtkComboBoxText *combo_box, - int position, - const char *text); -GDK_AVAILABLE_IN_ALL -void gtk_combo_box_text_prepend_text (GtkComboBoxText *combo_box, - const char *text); -GDK_AVAILABLE_IN_ALL -void gtk_combo_box_text_remove (GtkComboBoxText *combo_box, - int position); -GDK_AVAILABLE_IN_ALL -void gtk_combo_box_text_remove_all (GtkComboBoxText *combo_box); -GDK_AVAILABLE_IN_ALL -char *gtk_combo_box_text_get_active_text (GtkComboBoxText *combo_box); - -GDK_AVAILABLE_IN_ALL -void gtk_combo_box_text_insert (GtkComboBoxText *combo_box, - int position, - const char *id, - const char *text); -GDK_AVAILABLE_IN_ALL -void gtk_combo_box_text_append (GtkComboBoxText *combo_box, - const char *id, - const char *text); -GDK_AVAILABLE_IN_ALL -void gtk_combo_box_text_prepend (GtkComboBoxText *combo_box, - const char *id, - const char *text); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkComboBoxText, g_object_unref) - -G_END_DECLS - -#endif /* __GTK_COMBO_BOX_TEXT_H__ */ diff --git a/gtk/gtkcustompaperunixdialog.c b/gtk/gtkcustompaperunixdialog.c index 35d379484a..08238d3642 100644 --- a/gtk/gtkcustompaperunixdialog.c +++ b/gtk/gtkcustompaperunixdialog.c @@ -28,7 +28,7 @@ #include #include "gtkprivate.h" -#include "gtkliststore.h" +#include "deprecated/gtkliststore.h" #include "gtksignallistitemfactory.h" #include "gtklabel.h" diff --git a/gtk/gtkentry.c b/gtk/gtkentry.c index e66812ba18..35a1cd53db 100644 --- a/gtk/gtkentry.c +++ b/gtk/gtkentry.c @@ -34,8 +34,7 @@ #include "gtkadjustment.h" #include "gtkbox.h" #include "gtkbutton.h" -#include "gtkcelleditable.h" -#include "gtkcelllayout.h" +#include "deprecated/gtkcelleditable.h" #include "gtkdebug.h" #include "gtkeditable.h" #include "gtkemojichooser.h" @@ -1859,6 +1858,9 @@ gtk_entry_direction_changed (GtkWidget *widget, /* GtkCellEditable method implementations */ + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + static void gtk_cell_editable_entry_activated (GtkEntry *entry, gpointer data) { @@ -1908,6 +1910,8 @@ gtk_entry_start_editing (GtkCellEditable *cell_editable, cell_editable); } +G_GNUC_END_IGNORE_DEPRECATIONS + /* Internal functions */ diff --git a/gtk/gtkentryprivate.h b/gtk/gtkentryprivate.h index 8415d5dd0b..9be1d879ea 100644 --- a/gtk/gtkentryprivate.h +++ b/gtk/gtkentryprivate.h @@ -22,9 +22,9 @@ #include "deprecated/gtkentrycompletion.h" #include "gtkeventcontrollermotion.h" -#include "gtkliststore.h" -#include "gtktreemodelfilter.h" -#include "gtktreeviewcolumn.h" +#include "deprecated/gtkliststore.h" +#include "deprecated/gtktreemodelfilter.h" +#include "deprecated/gtktreeviewcolumn.h" #include "gtkeventcontrollerkey.h" #include "gtktextprivate.h" diff --git a/gtk/gtkfilechooserentry.c b/gtk/gtkfilechooserentry.c index f70de025a8..62d751fa86 100644 --- a/gtk/gtkfilechooserentry.c +++ b/gtk/gtkfilechooserentry.c @@ -22,8 +22,8 @@ #include -#include "gtkcelllayout.h" -#include "gtkcellrenderertext.h" +#include "deprecated/gtkcelllayout.h" +#include "deprecated/gtkcellrenderertext.h" #include "gtkentryprivate.h" #include "gtkfilechooserutils.h" #include "gtklabel.h" diff --git a/gtk/gtkfilechooserprivate.h b/gtk/gtkfilechooserprivate.h index 7b47b416b3..305ce7f76d 100644 --- a/gtk/gtkfilechooserprivate.h +++ b/gtk/gtkfilechooserprivate.h @@ -21,14 +21,14 @@ #include "gtkfilechooser.h" #include "gtkfilesystemmodel.h" -#include "gtkliststore.h" +#include "deprecated/gtkliststore.h" #include "gtkrecentmanager.h" #include "gtksearchengineprivate.h" #include "gtkquery.h" #include "gtksizegroup.h" -#include "gtktreemodelsort.h" -#include "gtktreestore.h" -#include "gtktreeview.h" +#include "deprecated/gtktreemodelsort.h" +#include "deprecated/gtktreestore.h" +#include "deprecated/gtktreeview.h" #include "gtkbox.h" G_BEGIN_DECLS diff --git a/gtk/gtkfilechooserwidget.c b/gtk/gtkfilechooserwidget.c index c86a08a4bf..8b00a40e65 100644 --- a/gtk/gtkfilechooserwidget.c +++ b/gtk/gtkfilechooserwidget.c @@ -24,9 +24,8 @@ #include "gtkbookmarksmanagerprivate.h" #include "gtkbutton.h" -#include "gtkcelllayout.h" -#include "gtkcellrendererpixbuf.h" -#include "gtkcellrenderertext.h" +#include "deprecated/gtkcellrendererpixbuf.h" +#include "deprecated/gtkcellrenderertext.h" #include "gtkdropdown.h" #include "gtkcssnumbervalueprivate.h" #include "gtkdragsource.h" @@ -56,9 +55,9 @@ #include "gtksizerequest.h" #include "gtkstack.h" #include "gtktooltip.h" -#include "gtktreednd.h" -#include "gtktreeprivate.h" -#include "gtktreeselection.h" +#include "deprecated/gtktreednd.h" +#include "deprecated/gtktreeprivate.h" +#include "deprecated/gtktreeselection.h" #include "gtkbox.h" #include "gtkcheckbutton.h" #include "gtkwindowgroup.h" @@ -97,6 +96,8 @@ #include #endif +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + /** * GtkFileChooserWidget: * diff --git a/gtk/gtkfilesystemmodel.c b/gtk/gtkfilesystemmodel.c index 46d7f10b91..20b670e774 100644 --- a/gtk/gtkfilesystemmodel.c +++ b/gtk/gtkfilesystemmodel.c @@ -25,12 +25,14 @@ #include "gtkfilechooserutils.h" #include "gtkmarshalers.h" -#include "gtktreedatalistprivate.h" -#include "gtktreednd.h" -#include "gtktreemodel.h" +#include "deprecated/gtktreedatalistprivate.h" +#include "deprecated/gtktreednd.h" +#include "deprecated/gtktreemodel.h" #include "gtkfilter.h" #include "gtkprivate.h" +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + /*** Structure: how GtkFileSystemModel works * * This is a custom GtkTreeModel used to hold a collection of files for GtkFileChooser. There are two use cases: diff --git a/gtk/gtkfilesystemmodel.h b/gtk/gtkfilesystemmodel.h index d33d30035d..05383a7328 100644 --- a/gtk/gtkfilesystemmodel.h +++ b/gtk/gtkfilesystemmodel.h @@ -21,7 +21,7 @@ #include #include -#include +#include G_BEGIN_DECLS diff --git a/gtk/gtkiconview.c b/gtk/gtkiconview.c deleted file mode 100644 index 2def92cd4a..0000000000 --- a/gtk/gtkiconview.c +++ /dev/null @@ -1,6672 +0,0 @@ -/* gtkiconview.c - * Copyright (C) 2002, 2004 Anders Carlsson - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#include "config.h" - -#include "gtkiconviewprivate.h" - -#include "gtkadjustmentprivate.h" -#include "gtkcellareabox.h" -#include "gtkcellareacontext.h" -#include "gtkcelllayout.h" -#include "gtkcellrenderer.h" -#include "gtkcellrendererpixbuf.h" -#include "gtkcellrenderertext.h" -#include "gtkdragsourceprivate.h" -#include "gtkentry.h" -#include "gtkmain.h" -#include "gtkmarshalers.h" -#include "gtkorientable.h" -#include "gtkprivate.h" -#include "gtkscrollable.h" -#include "gtksizerequest.h" -#include "gtksnapshot.h" -#include "gtkstylecontextprivate.h" -#include "gtktreednd.h" -#include "gtktypebuiltins.h" -#include "gtkwidgetprivate.h" -#include "gtkwindow.h" -#include "gtkeventcontrollerkey.h" -#include "gtkdragsource.h" -#include "gtkdragicon.h" -#include "gtknative.h" - -#include - -/** - * GtkIconView: - * - * `GtkIconView` is a widget which displays data in a grid of icons. - * - * `GtkIconView` provides an alternative view on a `GtkTreeModel`. - * It displays the model as a grid of icons with labels. Like - * [class@Gtk.TreeView], it allows to select one or multiple items - * (depending on the selection mode, see [method@Gtk.IconView.set_selection_mode]). - * In addition to selection with the arrow keys, `GtkIconView` supports - * rubberband selection, which is controlled by dragging the pointer. - * - * Note that if the tree model is backed by an actual tree store (as - * opposed to a flat list where the mapping to icons is obvious), - * `GtkIconView` will only display the first level of the tree and - * ignore the tree’s branches. - * - * # CSS nodes - * - * ``` - * iconview.view - * ╰── [rubberband] - * ``` - * - * `GtkIconView` has a single CSS node with name iconview and style class .view. - * For rubberband selection, a subnode with name rubberband is used. - */ - -#define SCROLL_EDGE_SIZE 15 - -typedef struct _GtkIconViewChild GtkIconViewChild; -struct _GtkIconViewChild -{ - GtkWidget *widget; - GdkRectangle area; -}; - -/* Signals */ -enum -{ - ITEM_ACTIVATED, - SELECTION_CHANGED, - SELECT_ALL, - UNSELECT_ALL, - SELECT_CURSOR_ITEM, - TOGGLE_CURSOR_ITEM, - MOVE_CURSOR, - ACTIVATE_CURSOR_ITEM, - LAST_SIGNAL -}; - -/* Properties */ -enum -{ - PROP_0, - PROP_PIXBUF_COLUMN, - PROP_TEXT_COLUMN, - PROP_MARKUP_COLUMN, - PROP_SELECTION_MODE, - PROP_ITEM_ORIENTATION, - PROP_MODEL, - PROP_COLUMNS, - PROP_ITEM_WIDTH, - PROP_SPACING, - PROP_ROW_SPACING, - PROP_COLUMN_SPACING, - PROP_MARGIN, - PROP_REORDERABLE, - PROP_TOOLTIP_COLUMN, - PROP_ITEM_PADDING, - PROP_CELL_AREA, - PROP_HADJUSTMENT, - PROP_VADJUSTMENT, - PROP_HSCROLL_POLICY, - PROP_VSCROLL_POLICY, - PROP_ACTIVATE_ON_SINGLE_CLICK -}; - -/* GObject vfuncs */ -static void gtk_icon_view_cell_layout_init (GtkCellLayoutIface *iface); -static void gtk_icon_view_dispose (GObject *object); -static void gtk_icon_view_constructed (GObject *object); -static void gtk_icon_view_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec); -static void gtk_icon_view_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec); -/* GtkWidget vfuncs */ -static GtkSizeRequestMode gtk_icon_view_get_request_mode (GtkWidget *widget); -static void gtk_icon_view_measure (GtkWidget *widget, - GtkOrientation orientation, - int for_size, - int *minimum, - int *natural, - int *minimum_baseline, - int *natural_baseline); -static void gtk_icon_view_size_allocate (GtkWidget *widget, - int width, - int height, - int baseline); -static void gtk_icon_view_snapshot (GtkWidget *widget, - GtkSnapshot *snapshot); -static void gtk_icon_view_motion (GtkEventController *controller, - double x, - double y, - gpointer user_data); -static void gtk_icon_view_leave (GtkEventController *controller, - gpointer user_data); -static void gtk_icon_view_button_press (GtkGestureClick *gesture, - int n_press, - double x, - double y, - gpointer user_data); -static void gtk_icon_view_button_release (GtkGestureClick *gesture, - int n_press, - double x, - double y, - gpointer user_data); -static gboolean gtk_icon_view_key_pressed (GtkEventControllerKey *controller, - guint keyval, - guint keycode, - GdkModifierType state, - GtkWidget *widget); - -static void gtk_icon_view_remove (GtkIconView *icon_view, - GtkWidget *widget); - -/* GtkIconView vfuncs */ -static void gtk_icon_view_real_select_all (GtkIconView *icon_view); -static void gtk_icon_view_real_unselect_all (GtkIconView *icon_view); -static void gtk_icon_view_real_select_cursor_item (GtkIconView *icon_view); -static void gtk_icon_view_real_toggle_cursor_item (GtkIconView *icon_view); -static gboolean gtk_icon_view_real_activate_cursor_item (GtkIconView *icon_view); - - /* Internal functions */ -static void gtk_icon_view_set_hadjustment_values (GtkIconView *icon_view); -static void gtk_icon_view_set_vadjustment_values (GtkIconView *icon_view); -static void gtk_icon_view_set_hadjustment (GtkIconView *icon_view, - GtkAdjustment *adjustment); -static void gtk_icon_view_set_vadjustment (GtkIconView *icon_view, - GtkAdjustment *adjustment); -static void gtk_icon_view_adjustment_changed (GtkAdjustment *adjustment, - GtkIconView *icon_view); -static void gtk_icon_view_layout (GtkIconView *icon_view); -static void gtk_icon_view_snapshot_item (GtkIconView *icon_view, - GtkSnapshot *snapshot, - GtkIconViewItem *item, - int x, - int y, - gboolean draw_focus); -static void gtk_icon_view_snapshot_rubberband (GtkIconView *icon_view, - GtkSnapshot *snapshot); -static void gtk_icon_view_queue_draw_path (GtkIconView *icon_view, - GtkTreePath *path); -static void gtk_icon_view_queue_draw_item (GtkIconView *icon_view, - GtkIconViewItem *item); -static void gtk_icon_view_start_rubberbanding (GtkIconView *icon_view, - GdkDevice *device, - int x, - int y); -static void gtk_icon_view_stop_rubberbanding (GtkIconView *icon_view); -static void gtk_icon_view_update_rubberband_selection (GtkIconView *icon_view); -static gboolean gtk_icon_view_item_hit_test (GtkIconView *icon_view, - GtkIconViewItem *item, - int x, - int y, - int width, - int height); -static gboolean gtk_icon_view_unselect_all_internal (GtkIconView *icon_view); -static void gtk_icon_view_update_rubberband (GtkIconView *icon_view); -static void gtk_icon_view_item_invalidate_size (GtkIconViewItem *item); -static void gtk_icon_view_invalidate_sizes (GtkIconView *icon_view); -static void gtk_icon_view_add_move_binding (GtkWidgetClass *widget_class, - guint keyval, - guint modmask, - GtkMovementStep step, - int count); -static gboolean gtk_icon_view_real_move_cursor (GtkIconView *icon_view, - GtkMovementStep step, - int count, - gboolean extend, - gboolean modify); -static void gtk_icon_view_move_cursor_up_down (GtkIconView *icon_view, - int count); -static void gtk_icon_view_move_cursor_page_up_down (GtkIconView *icon_view, - int count); -static void gtk_icon_view_move_cursor_left_right (GtkIconView *icon_view, - int count); -static void gtk_icon_view_move_cursor_start_end (GtkIconView *icon_view, - int count); -static void gtk_icon_view_scroll_to_item (GtkIconView *icon_view, - GtkIconViewItem *item); -static gboolean gtk_icon_view_select_all_between (GtkIconView *icon_view, - GtkIconViewItem *anchor, - GtkIconViewItem *cursor); - -static void gtk_icon_view_ensure_cell_area (GtkIconView *icon_view, - GtkCellArea *cell_area); - -static GtkCellArea *gtk_icon_view_cell_layout_get_area (GtkCellLayout *layout); - -static void gtk_icon_view_add_editable (GtkCellArea *area, - GtkCellRenderer *renderer, - GtkCellEditable *editable, - GdkRectangle *cell_area, - const char *path, - GtkIconView *icon_view); -static void gtk_icon_view_remove_editable (GtkCellArea *area, - GtkCellRenderer *renderer, - GtkCellEditable *editable, - GtkIconView *icon_view); -static void update_text_cell (GtkIconView *icon_view); -static void update_pixbuf_cell (GtkIconView *icon_view); - -/* Source side drag signals */ -static void gtk_icon_view_dnd_finished_cb (GdkDrag *drag, - GtkWidget *widget); -static GdkContentProvider * gtk_icon_view_drag_data_get (GtkIconView *icon_view, - GtkTreePath *source_row); - -/* Target side drag signals */ -static void gtk_icon_view_drag_leave (GtkDropTargetAsync *dest, - GdkDrop *drop, - GtkIconView *icon_view); -static GdkDragAction gtk_icon_view_drag_motion (GtkDropTargetAsync *dest, - GdkDrop *drop, - double x, - double y, - GtkIconView *icon_view); -static gboolean gtk_icon_view_drag_drop (GtkDropTargetAsync *dest, - GdkDrop *drop, - double x, - double y, - GtkIconView *icon_view); -static void gtk_icon_view_drag_data_received (GObject *source, - GAsyncResult *result, - gpointer data); -static gboolean gtk_icon_view_maybe_begin_drag (GtkIconView *icon_view, - double x, - double y, - GdkDevice *device); - -static void remove_scroll_timeout (GtkIconView *icon_view); - -/* GtkBuildable */ -static GtkBuildableIface *parent_buildable_iface; -static void gtk_icon_view_buildable_init (GtkBuildableIface *iface); -static gboolean gtk_icon_view_buildable_custom_tag_start (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const char *tagname, - GtkBuildableParser *parser, - gpointer *data); -static void gtk_icon_view_buildable_custom_tag_end (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const char *tagname, - gpointer data); - - -static guint icon_view_signals[LAST_SIGNAL] = { 0 }; - -G_DEFINE_TYPE_WITH_CODE (GtkIconView, gtk_icon_view, GTK_TYPE_WIDGET, - G_ADD_PRIVATE (GtkIconView) - G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT, - gtk_icon_view_cell_layout_init) - G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, - gtk_icon_view_buildable_init) - G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL)) - -static void -gtk_icon_view_class_init (GtkIconViewClass *klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - - gobject_class->constructed = gtk_icon_view_constructed; - gobject_class->dispose = gtk_icon_view_dispose; - gobject_class->set_property = gtk_icon_view_set_property; - gobject_class->get_property = gtk_icon_view_get_property; - - widget_class->get_request_mode = gtk_icon_view_get_request_mode; - widget_class->measure = gtk_icon_view_measure; - widget_class->size_allocate = gtk_icon_view_size_allocate; - widget_class->snapshot = gtk_icon_view_snapshot; - widget_class->focus = gtk_widget_focus_self; - widget_class->grab_focus = gtk_widget_grab_focus_self; - - klass->select_all = gtk_icon_view_real_select_all; - klass->unselect_all = gtk_icon_view_real_unselect_all; - klass->select_cursor_item = gtk_icon_view_real_select_cursor_item; - klass->toggle_cursor_item = gtk_icon_view_real_toggle_cursor_item; - klass->activate_cursor_item = gtk_icon_view_real_activate_cursor_item; - klass->move_cursor = gtk_icon_view_real_move_cursor; - - /* Properties */ - /** - * GtkIconView:selection-mode: - * - * The ::selection-mode property specifies the selection mode of - * icon view. If the mode is %GTK_SELECTION_MULTIPLE, rubberband selection - * is enabled, for the other modes, only keyboard selection is possible. - */ - g_object_class_install_property (gobject_class, - PROP_SELECTION_MODE, - g_param_spec_enum ("selection-mode", NULL, NULL, - GTK_TYPE_SELECTION_MODE, - GTK_SELECTION_SINGLE, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - /** - * GtkIconView:pixbuf-column: - * - * The ::pixbuf-column property contains the number of the model column - * containing the pixbufs which are displayed. The pixbuf column must be - * of type `GDK_TYPE_PIXBUF`. Setting this property to -1 turns off the - * display of pixbufs. - */ - g_object_class_install_property (gobject_class, - PROP_PIXBUF_COLUMN, - g_param_spec_int ("pixbuf-column", NULL, NULL, - -1, G_MAXINT, -1, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - /** - * GtkIconView:text-column: - * - * The ::text-column property contains the number of the model column - * containing the texts which are displayed. The text column must be - * of type `G_TYPE_STRING`. If this property and the :markup-column - * property are both set to -1, no texts are displayed. - */ - g_object_class_install_property (gobject_class, - PROP_TEXT_COLUMN, - g_param_spec_int ("text-column", NULL, NULL, - -1, G_MAXINT, -1, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - - /** - * GtkIconView:markup-column: - * - * The ::markup-column property contains the number of the model column - * containing markup information to be displayed. The markup column must be - * of type `G_TYPE_STRING`. If this property and the :text-column property - * are both set to column numbers, it overrides the text column. - * If both are set to -1, no texts are displayed. - */ - g_object_class_install_property (gobject_class, - PROP_MARKUP_COLUMN, - g_param_spec_int ("markup-column", NULL, NULL, - -1, G_MAXINT, -1, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - g_object_class_install_property (gobject_class, - PROP_MODEL, - g_param_spec_object ("model", NULL, NULL, - GTK_TYPE_TREE_MODEL, - GTK_PARAM_READWRITE)); - - /** - * GtkIconView:columns: - * - * The columns property contains the number of the columns in which the - * items should be displayed. If it is -1, the number of columns will - * be chosen automatically to fill the available area. - */ - g_object_class_install_property (gobject_class, - PROP_COLUMNS, - g_param_spec_int ("columns", NULL, NULL, - -1, G_MAXINT, -1, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - - /** - * GtkIconView:item-width: - * - * The item-width property specifies the width to use for each item. - * If it is set to -1, the icon view will automatically determine a - * suitable item size. - */ - g_object_class_install_property (gobject_class, - PROP_ITEM_WIDTH, - g_param_spec_int ("item-width", NULL, NULL, - -1, G_MAXINT, -1, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - /** - * GtkIconView:spacing: - * - * The spacing property specifies the space which is inserted between - * the cells (i.e. the icon and the text) of an item. - */ - g_object_class_install_property (gobject_class, - PROP_SPACING, - g_param_spec_int ("spacing", NULL, NULL, - 0, G_MAXINT, 0, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - /** - * GtkIconView:row-spacing: - * - * The row-spacing property specifies the space which is inserted between - * the rows of the icon view. - */ - g_object_class_install_property (gobject_class, - PROP_ROW_SPACING, - g_param_spec_int ("row-spacing", NULL, NULL, - 0, G_MAXINT, 6, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - /** - * GtkIconView:column-spacing: - * - * The column-spacing property specifies the space which is inserted between - * the columns of the icon view. - */ - g_object_class_install_property (gobject_class, - PROP_COLUMN_SPACING, - g_param_spec_int ("column-spacing", NULL, NULL, - 0, G_MAXINT, 6, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - /** - * GtkIconView:margin: - * - * The margin property specifies the space which is inserted - * at the edges of the icon view. - */ - g_object_class_install_property (gobject_class, - PROP_MARGIN, - g_param_spec_int ("margin", NULL, NULL, - 0, G_MAXINT, 6, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - /** - * GtkIconView:item-orientation: - * - * The item-orientation property specifies how the cells (i.e. the icon and - * the text) of the item are positioned relative to each other. - */ - g_object_class_install_property (gobject_class, - PROP_ITEM_ORIENTATION, - g_param_spec_enum ("item-orientation", NULL, NULL, - GTK_TYPE_ORIENTATION, - GTK_ORIENTATION_VERTICAL, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - /** - * GtkIconView:reorderable: - * - * The reorderable property specifies if the items can be reordered - * by DND. - */ - g_object_class_install_property (gobject_class, - PROP_REORDERABLE, - g_param_spec_boolean ("reorderable", NULL, NULL, - FALSE, - G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - g_object_class_install_property (gobject_class, - PROP_TOOLTIP_COLUMN, - g_param_spec_int ("tooltip-column", NULL, NULL, - -1, - G_MAXINT, - -1, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - /** - * GtkIconView:item-padding: - * - * The item-padding property specifies the padding around each - * of the icon view's item. - */ - g_object_class_install_property (gobject_class, - PROP_ITEM_PADDING, - g_param_spec_int ("item-padding", NULL, NULL, - 0, G_MAXINT, 6, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - /** - * GtkIconView:cell-area: - * - * The `GtkCellArea` used to layout cell renderers for this view. - * - * If no area is specified when creating the icon view with gtk_icon_view_new_with_area() - * a `GtkCellAreaBox` will be used. - */ - g_object_class_install_property (gobject_class, - PROP_CELL_AREA, - g_param_spec_object ("cell-area", NULL, NULL, - GTK_TYPE_CELL_AREA, - GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - - /** - * GtkIconView:activate-on-single-click: - * - * The activate-on-single-click property specifies whether the "item-activated" signal - * will be emitted after a single click. - */ - g_object_class_install_property (gobject_class, - PROP_ACTIVATE_ON_SINGLE_CLICK, - g_param_spec_boolean ("activate-on-single-click", NULL, NULL, - FALSE, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - /* Scrollable interface properties */ - g_object_class_override_property (gobject_class, PROP_HADJUSTMENT, "hadjustment"); - g_object_class_override_property (gobject_class, PROP_VADJUSTMENT, "vadjustment"); - g_object_class_override_property (gobject_class, PROP_HSCROLL_POLICY, "hscroll-policy"); - g_object_class_override_property (gobject_class, PROP_VSCROLL_POLICY, "vscroll-policy"); - - /* Signals */ - /** - * GtkIconView::item-activated: - * @iconview: the object on which the signal is emitted - * @path: the `GtkTreePath` for the activated item - * - * The ::item-activated signal is emitted when the method - * gtk_icon_view_item_activated() is called, when the user double - * clicks an item with the "activate-on-single-click" property set - * to %FALSE, or when the user single clicks an item when the - * "activate-on-single-click" property set to %TRUE. It is also - * emitted when a non-editable item is selected and one of the keys: - * Space, Return or Enter is pressed. - */ - icon_view_signals[ITEM_ACTIVATED] = - g_signal_new (I_("item-activated"), - G_TYPE_FROM_CLASS (gobject_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkIconViewClass, item_activated), - NULL, NULL, - NULL, - G_TYPE_NONE, 1, - GTK_TYPE_TREE_PATH); - - /** - * GtkIconView::selection-changed: - * @iconview: the object on which the signal is emitted - * - * The ::selection-changed signal is emitted when the selection - * (i.e. the set of selected items) changes. - */ - icon_view_signals[SELECTION_CHANGED] = - g_signal_new (I_("selection-changed"), - G_TYPE_FROM_CLASS (gobject_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (GtkIconViewClass, selection_changed), - NULL, NULL, - NULL, - G_TYPE_NONE, 0); - - /** - * GtkIconView::select-all: - * @iconview: the object on which the signal is emitted - * - * A [keybinding signal][class@Gtk.SignalAction] - * which gets emitted when the user selects all items. - * - * Applications should not connect to it, but may emit it with - * g_signal_emit_by_name() if they need to control selection - * programmatically. - * - * The default binding for this signal is Ctrl-a. - */ - icon_view_signals[SELECT_ALL] = - g_signal_new (I_("select-all"), - G_TYPE_FROM_CLASS (gobject_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (GtkIconViewClass, select_all), - NULL, NULL, - NULL, - G_TYPE_NONE, 0); - - /** - * GtkIconView::unselect-all: - * @iconview: the object on which the signal is emitted - * - * A [keybinding signal][class@Gtk.SignalAction] - * which gets emitted when the user unselects all items. - * - * Applications should not connect to it, but may emit it with - * g_signal_emit_by_name() if they need to control selection - * programmatically. - * - * The default binding for this signal is Ctrl-Shift-a. - */ - icon_view_signals[UNSELECT_ALL] = - g_signal_new (I_("unselect-all"), - G_TYPE_FROM_CLASS (gobject_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (GtkIconViewClass, unselect_all), - NULL, NULL, - NULL, - G_TYPE_NONE, 0); - - /** - * GtkIconView::select-cursor-item: - * @iconview: the object on which the signal is emitted - * - * A [keybinding signal][class@Gtk.SignalAction] - * which gets emitted when the user selects the item that is currently - * focused. - * - * Applications should not connect to it, but may emit it with - * g_signal_emit_by_name() if they need to control selection - * programmatically. - * - * There is no default binding for this signal. - */ - icon_view_signals[SELECT_CURSOR_ITEM] = - g_signal_new (I_("select-cursor-item"), - G_TYPE_FROM_CLASS (gobject_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (GtkIconViewClass, select_cursor_item), - NULL, NULL, - NULL, - G_TYPE_NONE, 0); - - /** - * GtkIconView::toggle-cursor-item: - * @iconview: the object on which the signal is emitted - * - * A [keybinding signal][class@Gtk.SignalAction] - * which gets emitted when the user toggles whether the currently - * focused item is selected or not. The exact effect of this - * depend on the selection mode. - * - * Applications should not connect to it, but may emit it with - * g_signal_emit_by_name() if they need to control selection - * programmatically. - * - * There is no default binding for this signal is Ctrl-Space. - */ - icon_view_signals[TOGGLE_CURSOR_ITEM] = - g_signal_new (I_("toggle-cursor-item"), - G_TYPE_FROM_CLASS (gobject_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (GtkIconViewClass, toggle_cursor_item), - NULL, NULL, - NULL, - G_TYPE_NONE, 0); - - /** - * GtkIconView::activate-cursor-item: - * @iconview: the object on which the signal is emitted - * - * A [keybinding signal][class@Gtk.SignalAction] - * which gets emitted when the user activates the currently - * focused item. - * - * Applications should not connect to it, but may emit it with - * g_signal_emit_by_name() if they need to control activation - * programmatically. - * - * The default bindings for this signal are Space, Return and Enter. - */ - icon_view_signals[ACTIVATE_CURSOR_ITEM] = - g_signal_new (I_("activate-cursor-item"), - G_TYPE_FROM_CLASS (gobject_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (GtkIconViewClass, activate_cursor_item), - NULL, NULL, - _gtk_marshal_BOOLEAN__VOID, - G_TYPE_BOOLEAN, 0); - g_signal_set_va_marshaller (icon_view_signals[ACTIVATE_CURSOR_ITEM], - G_TYPE_FROM_CLASS (klass), - _gtk_marshal_BOOLEAN__VOIDv); - - /** - * GtkIconView::move-cursor: - * @iconview: the object which received the signal - * @step: the granularity of the move, as a `GtkMovementStep` - * @count: the number of @step units to move - * @extend: whether to extend the selection - * @modify: whether to modify the selection - * - * The ::move-cursor signal is a - * [keybinding signal][class@Gtk.SignalAction] - * which gets emitted when the user initiates a cursor movement. - * - * Applications should not connect to it, but may emit it with - * g_signal_emit_by_name() if they need to control the cursor - * programmatically. - * - * The default bindings for this signal include - * - Arrow keys which move by individual steps - * - Home/End keys which move to the first/last item - * - PageUp/PageDown which move by "pages" - * All of these will extend the selection when combined with - * the Shift modifier. - */ - icon_view_signals[MOVE_CURSOR] = - g_signal_new (I_("move-cursor"), - G_TYPE_FROM_CLASS (gobject_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (GtkIconViewClass, move_cursor), - NULL, NULL, - _gtk_marshal_BOOLEAN__ENUM_INT_BOOLEAN_BOOLEAN, - G_TYPE_BOOLEAN, 4, - GTK_TYPE_MOVEMENT_STEP, - G_TYPE_INT, - G_TYPE_BOOLEAN, - G_TYPE_BOOLEAN); - g_signal_set_va_marshaller (icon_view_signals[MOVE_CURSOR], - G_TYPE_FROM_CLASS (klass), - _gtk_marshal_BOOLEAN__ENUM_INT_BOOLEAN_BOOLEANv); - - /* Key bindings */ - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_a, GDK_CONTROL_MASK, - "select-all", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_a, GDK_CONTROL_MASK | GDK_SHIFT_MASK, - "unselect-all", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_space, GDK_CONTROL_MASK, - "toggle-cursor-item", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Space, GDK_CONTROL_MASK, - "toggle-cursor-item", - NULL); - - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_space, 0, - "activate-cursor-item", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Space, 0, - "activate-cursor-item", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Return, 0, - "activate-cursor-item", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_ISO_Enter, 0, - "activate-cursor-item", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Enter, 0, - "activate-cursor-item", - NULL); - - gtk_icon_view_add_move_binding (widget_class, GDK_KEY_Up, 0, - GTK_MOVEMENT_DISPLAY_LINES, -1); - gtk_icon_view_add_move_binding (widget_class, GDK_KEY_KP_Up, 0, - GTK_MOVEMENT_DISPLAY_LINES, -1); - - gtk_icon_view_add_move_binding (widget_class, GDK_KEY_Down, 0, - GTK_MOVEMENT_DISPLAY_LINES, 1); - gtk_icon_view_add_move_binding (widget_class, GDK_KEY_KP_Down, 0, - GTK_MOVEMENT_DISPLAY_LINES, 1); - - gtk_icon_view_add_move_binding (widget_class, GDK_KEY_p, GDK_CONTROL_MASK, - GTK_MOVEMENT_DISPLAY_LINES, -1); - - gtk_icon_view_add_move_binding (widget_class, GDK_KEY_n, GDK_CONTROL_MASK, - GTK_MOVEMENT_DISPLAY_LINES, 1); - - gtk_icon_view_add_move_binding (widget_class, GDK_KEY_Home, 0, - GTK_MOVEMENT_BUFFER_ENDS, -1); - gtk_icon_view_add_move_binding (widget_class, GDK_KEY_KP_Home, 0, - GTK_MOVEMENT_BUFFER_ENDS, -1); - - gtk_icon_view_add_move_binding (widget_class, GDK_KEY_End, 0, - GTK_MOVEMENT_BUFFER_ENDS, 1); - gtk_icon_view_add_move_binding (widget_class, GDK_KEY_KP_End, 0, - GTK_MOVEMENT_BUFFER_ENDS, 1); - - gtk_icon_view_add_move_binding (widget_class, GDK_KEY_Page_Up, 0, - GTK_MOVEMENT_PAGES, -1); - gtk_icon_view_add_move_binding (widget_class, GDK_KEY_KP_Page_Up, 0, - GTK_MOVEMENT_PAGES, -1); - - gtk_icon_view_add_move_binding (widget_class, GDK_KEY_Page_Down, 0, - GTK_MOVEMENT_PAGES, 1); - gtk_icon_view_add_move_binding (widget_class, GDK_KEY_KP_Page_Down, 0, - GTK_MOVEMENT_PAGES, 1); - - gtk_icon_view_add_move_binding (widget_class, GDK_KEY_Right, 0, - GTK_MOVEMENT_VISUAL_POSITIONS, 1); - gtk_icon_view_add_move_binding (widget_class, GDK_KEY_Left, 0, - GTK_MOVEMENT_VISUAL_POSITIONS, -1); - - gtk_icon_view_add_move_binding (widget_class, GDK_KEY_KP_Right, 0, - GTK_MOVEMENT_VISUAL_POSITIONS, 1); - gtk_icon_view_add_move_binding (widget_class, GDK_KEY_KP_Left, 0, - GTK_MOVEMENT_VISUAL_POSITIONS, -1); - - gtk_widget_class_set_css_name (widget_class, I_("iconview")); -} - -static void -gtk_icon_view_buildable_add_child (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const char *type) -{ - if (GTK_IS_CELL_RENDERER (child)) - _gtk_cell_layout_buildable_add_child (buildable, builder, child, type); - else - parent_buildable_iface->add_child (buildable, builder, child, type); -} - -static void -gtk_icon_view_buildable_init (GtkBuildableIface *iface) -{ - parent_buildable_iface = g_type_interface_peek_parent (iface); - iface->add_child = gtk_icon_view_buildable_add_child; - iface->custom_tag_start = gtk_icon_view_buildable_custom_tag_start; - iface->custom_tag_end = gtk_icon_view_buildable_custom_tag_end; -} - -static void -gtk_icon_view_cell_layout_init (GtkCellLayoutIface *iface) -{ - iface->get_area = gtk_icon_view_cell_layout_get_area; -} - -static void -gtk_icon_view_init (GtkIconView *icon_view) -{ - GtkEventController *controller; - GtkGesture *gesture; - - icon_view->priv = gtk_icon_view_get_instance_private (icon_view); - - icon_view->priv->width = 0; - icon_view->priv->height = 0; - icon_view->priv->selection_mode = GTK_SELECTION_SINGLE; - icon_view->priv->pressed_button = -1; - icon_view->priv->press_start_x = -1; - icon_view->priv->press_start_y = -1; - icon_view->priv->text_column = -1; - icon_view->priv->markup_column = -1; - icon_view->priv->pixbuf_column = -1; - icon_view->priv->text_cell = NULL; - icon_view->priv->pixbuf_cell = NULL; - icon_view->priv->tooltip_column = -1; - icon_view->priv->mouse_x = -1; - icon_view->priv->mouse_y = -1; - - gtk_widget_set_overflow (GTK_WIDGET (icon_view), GTK_OVERFLOW_HIDDEN); - gtk_widget_set_focusable (GTK_WIDGET (icon_view), TRUE); - - icon_view->priv->item_orientation = GTK_ORIENTATION_VERTICAL; - - icon_view->priv->columns = -1; - icon_view->priv->item_width = -1; - icon_view->priv->spacing = 0; - icon_view->priv->row_spacing = 6; - icon_view->priv->column_spacing = 6; - icon_view->priv->margin = 6; - icon_view->priv->item_padding = 6; - icon_view->priv->activate_on_single_click = FALSE; - - icon_view->priv->draw_focus = TRUE; - - icon_view->priv->row_contexts = - g_ptr_array_new_with_free_func ((GDestroyNotify)g_object_unref); - - gtk_widget_add_css_class (GTK_WIDGET (icon_view), "view"); - - gesture = gtk_gesture_click_new (); - g_signal_connect (gesture, "pressed", G_CALLBACK (gtk_icon_view_button_press), - icon_view); - g_signal_connect (gesture, "released", G_CALLBACK (gtk_icon_view_button_release), - icon_view); - gtk_widget_add_controller (GTK_WIDGET (icon_view), GTK_EVENT_CONTROLLER (gesture)); - - controller = gtk_event_controller_motion_new (); - g_signal_connect (controller, "leave", G_CALLBACK (gtk_icon_view_leave), icon_view); - g_signal_connect (controller, "motion", G_CALLBACK (gtk_icon_view_motion), icon_view); - gtk_widget_add_controller (GTK_WIDGET (icon_view), controller); - - controller = gtk_event_controller_key_new (); - g_signal_connect (controller, "key-pressed", G_CALLBACK (gtk_icon_view_key_pressed), - icon_view); - gtk_widget_add_controller (GTK_WIDGET (icon_view), controller); -} - -/* GObject methods */ - -static void -gtk_icon_view_constructed (GObject *object) -{ - GtkIconView *icon_view = GTK_ICON_VIEW (object); - - G_OBJECT_CLASS (gtk_icon_view_parent_class)->constructed (object); - - gtk_icon_view_ensure_cell_area (icon_view, NULL); -} - -static void -gtk_icon_view_dispose (GObject *object) -{ - GtkIconView *icon_view; - GtkIconViewPrivate *priv; - - icon_view = GTK_ICON_VIEW (object); - priv = icon_view->priv; - - gtk_icon_view_set_model (icon_view, NULL); - - if (icon_view->priv->scroll_to_path != NULL) - { - gtk_tree_row_reference_free (icon_view->priv->scroll_to_path); - icon_view->priv->scroll_to_path = NULL; - } - - remove_scroll_timeout (icon_view); - - if (icon_view->priv->hadjustment != NULL) - { - g_object_unref (icon_view->priv->hadjustment); - icon_view->priv->hadjustment = NULL; - } - - if (icon_view->priv->vadjustment != NULL) - { - g_object_unref (icon_view->priv->vadjustment); - icon_view->priv->vadjustment = NULL; - } - - if (priv->cell_area_context) - { - g_object_unref (priv->cell_area_context); - priv->cell_area_context = NULL; - } - - if (priv->row_contexts) - { - g_ptr_array_free (priv->row_contexts, TRUE); - priv->row_contexts = NULL; - } - - if (priv->cell_area) - { - gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); - - g_signal_handler_disconnect (priv->cell_area, priv->add_editable_id); - g_signal_handler_disconnect (priv->cell_area, priv->remove_editable_id); - priv->add_editable_id = 0; - priv->remove_editable_id = 0; - - g_object_unref (priv->cell_area); - priv->cell_area = NULL; - } - - g_clear_object (&priv->key_controller); - - g_clear_pointer (&priv->source_formats, gdk_content_formats_unref); - - G_OBJECT_CLASS (gtk_icon_view_parent_class)->dispose (object); -} - -static void -gtk_icon_view_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - GtkIconView *icon_view; - GtkCellArea *area; - - icon_view = GTK_ICON_VIEW (object); - - switch (prop_id) - { - case PROP_SELECTION_MODE: - gtk_icon_view_set_selection_mode (icon_view, g_value_get_enum (value)); - break; - case PROP_PIXBUF_COLUMN: - gtk_icon_view_set_pixbuf_column (icon_view, g_value_get_int (value)); - break; - case PROP_TEXT_COLUMN: - gtk_icon_view_set_text_column (icon_view, g_value_get_int (value)); - break; - case PROP_MARKUP_COLUMN: - gtk_icon_view_set_markup_column (icon_view, g_value_get_int (value)); - break; - case PROP_MODEL: - gtk_icon_view_set_model (icon_view, g_value_get_object (value)); - break; - case PROP_ITEM_ORIENTATION: - gtk_icon_view_set_item_orientation (icon_view, g_value_get_enum (value)); - break; - case PROP_COLUMNS: - gtk_icon_view_set_columns (icon_view, g_value_get_int (value)); - break; - case PROP_ITEM_WIDTH: - gtk_icon_view_set_item_width (icon_view, g_value_get_int (value)); - break; - case PROP_SPACING: - gtk_icon_view_set_spacing (icon_view, g_value_get_int (value)); - break; - case PROP_ROW_SPACING: - gtk_icon_view_set_row_spacing (icon_view, g_value_get_int (value)); - break; - case PROP_COLUMN_SPACING: - gtk_icon_view_set_column_spacing (icon_view, g_value_get_int (value)); - break; - case PROP_MARGIN: - gtk_icon_view_set_margin (icon_view, g_value_get_int (value)); - break; - case PROP_REORDERABLE: - gtk_icon_view_set_reorderable (icon_view, g_value_get_boolean (value)); - break; - - case PROP_TOOLTIP_COLUMN: - gtk_icon_view_set_tooltip_column (icon_view, g_value_get_int (value)); - break; - - case PROP_ITEM_PADDING: - gtk_icon_view_set_item_padding (icon_view, g_value_get_int (value)); - break; - - case PROP_ACTIVATE_ON_SINGLE_CLICK: - gtk_icon_view_set_activate_on_single_click (icon_view, g_value_get_boolean (value)); - break; - - case PROP_CELL_AREA: - /* Construct-only, can only be assigned once */ - area = g_value_get_object (value); - if (area) - { - if (icon_view->priv->cell_area != NULL) - { - g_warning ("cell-area has already been set, ignoring construct property"); - g_object_ref_sink (area); - g_object_unref (area); - } - else - gtk_icon_view_ensure_cell_area (icon_view, area); - } - break; - - case PROP_HADJUSTMENT: - gtk_icon_view_set_hadjustment (icon_view, g_value_get_object (value)); - break; - case PROP_VADJUSTMENT: - gtk_icon_view_set_vadjustment (icon_view, g_value_get_object (value)); - break; - case PROP_HSCROLL_POLICY: - if (icon_view->priv->hscroll_policy != g_value_get_enum (value)) - { - icon_view->priv->hscroll_policy = g_value_get_enum (value); - gtk_widget_queue_resize (GTK_WIDGET (icon_view)); - g_object_notify_by_pspec (object, pspec); - } - break; - case PROP_VSCROLL_POLICY: - if (icon_view->priv->vscroll_policy != g_value_get_enum (value)) - { - icon_view->priv->vscroll_policy = g_value_get_enum (value); - gtk_widget_queue_resize (GTK_WIDGET (icon_view)); - g_object_notify_by_pspec (object, pspec); - } - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gtk_icon_view_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GtkIconView *icon_view; - - icon_view = GTK_ICON_VIEW (object); - - switch (prop_id) - { - case PROP_SELECTION_MODE: - g_value_set_enum (value, icon_view->priv->selection_mode); - break; - case PROP_PIXBUF_COLUMN: - g_value_set_int (value, icon_view->priv->pixbuf_column); - break; - case PROP_TEXT_COLUMN: - g_value_set_int (value, icon_view->priv->text_column); - break; - case PROP_MARKUP_COLUMN: - g_value_set_int (value, icon_view->priv->markup_column); - break; - case PROP_MODEL: - g_value_set_object (value, icon_view->priv->model); - break; - case PROP_ITEM_ORIENTATION: - g_value_set_enum (value, icon_view->priv->item_orientation); - break; - case PROP_COLUMNS: - g_value_set_int (value, icon_view->priv->columns); - break; - case PROP_ITEM_WIDTH: - g_value_set_int (value, icon_view->priv->item_width); - break; - case PROP_SPACING: - g_value_set_int (value, icon_view->priv->spacing); - break; - case PROP_ROW_SPACING: - g_value_set_int (value, icon_view->priv->row_spacing); - break; - case PROP_COLUMN_SPACING: - g_value_set_int (value, icon_view->priv->column_spacing); - break; - case PROP_MARGIN: - g_value_set_int (value, icon_view->priv->margin); - break; - case PROP_REORDERABLE: - g_value_set_boolean (value, icon_view->priv->reorderable); - break; - case PROP_TOOLTIP_COLUMN: - g_value_set_int (value, icon_view->priv->tooltip_column); - break; - - case PROP_ITEM_PADDING: - g_value_set_int (value, icon_view->priv->item_padding); - break; - - case PROP_ACTIVATE_ON_SINGLE_CLICK: - g_value_set_boolean (value, icon_view->priv->activate_on_single_click); - break; - - case PROP_CELL_AREA: - g_value_set_object (value, icon_view->priv->cell_area); - break; - - case PROP_HADJUSTMENT: - g_value_set_object (value, icon_view->priv->hadjustment); - break; - case PROP_VADJUSTMENT: - g_value_set_object (value, icon_view->priv->vadjustment); - break; - case PROP_HSCROLL_POLICY: - g_value_set_enum (value, icon_view->priv->hscroll_policy); - break; - case PROP_VSCROLL_POLICY: - g_value_set_enum (value, icon_view->priv->vscroll_policy); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -/* GtkWidget methods */ - -static int -gtk_icon_view_get_n_items (GtkIconView *icon_view) -{ - GtkIconViewPrivate *priv = icon_view->priv; - - if (priv->model == NULL) - return 0; - - return gtk_tree_model_iter_n_children (priv->model, NULL); -} - -static void -adjust_wrap_width (GtkIconView *icon_view) -{ - if (icon_view->priv->text_cell) - { - int pixbuf_width, wrap_width; - - if (icon_view->priv->items && icon_view->priv->pixbuf_cell) - { - gtk_cell_renderer_get_preferred_width (icon_view->priv->pixbuf_cell, - GTK_WIDGET (icon_view), - &pixbuf_width, NULL); - } - else - { - pixbuf_width = 0; - } - - if (icon_view->priv->item_width >= 0) - { - if (icon_view->priv->item_orientation == GTK_ORIENTATION_VERTICAL) - { - wrap_width = icon_view->priv->item_width; - } - else - { - wrap_width = icon_view->priv->item_width - pixbuf_width; - } - - wrap_width -= 2 * icon_view->priv->item_padding * 2; - } - else - { - wrap_width = MAX (pixbuf_width * 2, 50); - } - - if (icon_view->priv->items && icon_view->priv->pixbuf_cell) - { - /* Here we go with the same old guess, try the icon size and set double - * the size of the first icon found in the list, naive but works much - * of the time */ - - wrap_width = MAX (wrap_width * 2, 50); - } - - g_object_set (icon_view->priv->text_cell, "wrap-width", wrap_width, NULL); - g_object_set (icon_view->priv->text_cell, "width", wrap_width, NULL); - } -} - -/* General notes about layout - * - * The icon view is layouted like this: - * - * +----------+ s +----------+ - * | padding | p | padding | - * | +------+ | a | +------+ | - * | | cell | | c | | cell | | - * | +------+ | i | +------+ | - * | | n | | - * +----------+ g +----------+ - * - * In size request and allocation code, there are 3 sizes that are used: - * * cell size - * This is the size returned by gtk_cell_area_get_preferred_foo(). In places - * where code is interacting with the cell area and renderers this is useful. - * * padded size - * This is the cell size plus the item padding on each side. - * * spaced size - * This is the padded size plus the spacing. This is what’s used for most - * calculations because it can (ab)use the following formula: - * iconview_size = 2 * margin + n_items * spaced_size - spacing - * So when reading this code and fixing my bugs where I confuse these two, be - * aware of this distinction. - */ -static void -cell_area_get_preferred_size (GtkIconView *icon_view, - GtkCellAreaContext *context, - GtkOrientation orientation, - int for_size, - int *minimum, - int *natural) -{ - if (orientation == GTK_ORIENTATION_HORIZONTAL) - { - if (for_size > 0) - gtk_cell_area_get_preferred_width_for_height (icon_view->priv->cell_area, - context, - GTK_WIDGET (icon_view), - for_size, - minimum, natural); - else - gtk_cell_area_get_preferred_width (icon_view->priv->cell_area, - context, - GTK_WIDGET (icon_view), - minimum, natural); - } - else - { - if (for_size > 0) - gtk_cell_area_get_preferred_height_for_width (icon_view->priv->cell_area, - context, - GTK_WIDGET (icon_view), - for_size, - minimum, natural); - else - gtk_cell_area_get_preferred_height (icon_view->priv->cell_area, - context, - GTK_WIDGET (icon_view), - minimum, natural); - } -} - -static gboolean -gtk_icon_view_is_empty (GtkIconView *icon_view) -{ - return icon_view->priv->items == NULL; -} - -static void -gtk_icon_view_get_preferred_item_size (GtkIconView *icon_view, - GtkOrientation orientation, - int for_size, - int *minimum, - int *natural) -{ - GtkIconViewPrivate *priv = icon_view->priv; - GtkCellAreaContext *context; - GList *items; - - g_assert (!gtk_icon_view_is_empty (icon_view)); - - context = gtk_cell_area_create_context (priv->cell_area); - - for_size -= 2 * priv->item_padding; - - if (for_size > 0) - { - /* This is necessary for the context to work properly */ - for (items = priv->items; items; items = items->next) - { - GtkIconViewItem *item = items->data; - - _gtk_icon_view_set_cell_data (icon_view, item); - cell_area_get_preferred_size (icon_view, context, 1 - orientation, -1, NULL, NULL); - } - } - - for (items = priv->items; items; items = items->next) - { - GtkIconViewItem *item = items->data; - - _gtk_icon_view_set_cell_data (icon_view, item); - if (items == priv->items) - adjust_wrap_width (icon_view); - cell_area_get_preferred_size (icon_view, context, orientation, for_size, NULL, NULL); - } - - if (orientation == GTK_ORIENTATION_HORIZONTAL) - { - if (for_size > 0) - gtk_cell_area_context_get_preferred_width_for_height (context, - for_size, - minimum, natural); - else - gtk_cell_area_context_get_preferred_width (context, - minimum, natural); - } - else - { - if (for_size > 0) - gtk_cell_area_context_get_preferred_height_for_width (context, - for_size, - minimum, natural); - else - gtk_cell_area_context_get_preferred_height (context, - minimum, natural); - } - - if (orientation == GTK_ORIENTATION_HORIZONTAL && priv->item_width >= 0) - { - if (minimum) - *minimum = MAX (*minimum, priv->item_width); - if (natural) - *natural = *minimum; - } - - if (minimum) - *minimum = MAX (1, *minimum + 2 * priv->item_padding); - if (natural) - *natural = MAX (1, *natural + 2 * priv->item_padding); - - g_object_unref (context); -} - -static void -gtk_icon_view_compute_n_items_for_size (GtkIconView *icon_view, - GtkOrientation orientation, - int size, - int *min_items, - int *min_item_size, - int *max_items, - int *max_item_size) -{ - GtkIconViewPrivate *priv = icon_view->priv; - int minimum, natural, spacing; - - g_return_if_fail (min_item_size == NULL || min_items != NULL); - g_return_if_fail (max_item_size == NULL || max_items != NULL); - g_return_if_fail (!gtk_icon_view_is_empty (icon_view)); - - gtk_icon_view_get_preferred_item_size (icon_view, orientation, -1, &minimum, &natural); - - if (orientation == GTK_ORIENTATION_HORIZONTAL) - spacing = priv->column_spacing; - else - spacing = priv->row_spacing; - - size -= 2 * priv->margin; - size += spacing; - minimum += spacing; - natural += spacing; - - if (priv->columns > 0) - { - if (orientation == GTK_ORIENTATION_HORIZONTAL) - { - if (min_items) - *min_items = priv->columns; - if (max_items) - *max_items = priv->columns; - } - else - { - int n_items = gtk_icon_view_get_n_items (icon_view); - - if (min_items) - *min_items = (n_items + priv->columns - 1) / priv->columns; - if (max_items) - *max_items = (n_items + priv->columns - 1) / priv->columns; - } - } - else - { - if (max_items) - { - if (size <= minimum) - *max_items = 1; - else - *max_items = size / minimum; - } - - if (min_items) - { - if (size <= natural) - *min_items = 1; - else - *min_items = size / natural; - } - } - - if (min_item_size) - { - *min_item_size = size / *min_items; - *min_item_size = CLAMP (*min_item_size, minimum, natural); - *min_item_size -= spacing; - *min_item_size -= 2 * priv->item_padding; - } - - if (max_item_size) - { - *max_item_size = size / *max_items; - *max_item_size = CLAMP (*max_item_size, minimum, natural); - *max_item_size -= spacing; - *max_item_size -= 2 * priv->item_padding; - } -} - -static GtkSizeRequestMode -gtk_icon_view_get_request_mode (GtkWidget *widget) -{ - return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH; -} - -static void -gtk_icon_view_measure (GtkWidget *widget, - GtkOrientation orientation, - int for_size, - int *minimum, - int *natural, - int *minimum_baseline, - int *natural_baseline) -{ - GtkIconView *icon_view = GTK_ICON_VIEW (widget); - GtkIconViewPrivate *priv = icon_view->priv; - GtkOrientation other_orientation = orientation == GTK_ORIENTATION_HORIZONTAL ? - GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL; - int item_min, item_nat, items = 0, item_size = 0, n_items; - - if (gtk_icon_view_is_empty (icon_view)) - { - *minimum = *natural = 2 * priv->margin; - return; - } - - n_items = gtk_icon_view_get_n_items (icon_view); - - if (for_size < 0) - { - gtk_icon_view_get_preferred_item_size (icon_view, orientation, -1, &item_min, &item_nat); - - if (priv->columns > 0) - { - int n_rows = (n_items + priv->columns - 1) / priv->columns; - - *minimum = item_min * n_rows + priv->row_spacing * (n_rows - 1); - *natural = item_nat * n_rows + priv->row_spacing * (n_rows - 1); - } - else - { - *minimum = item_min; - *natural = item_nat * n_items + priv->row_spacing * (n_items - 1); - } - } - else - { - gtk_icon_view_compute_n_items_for_size (icon_view, orientation, for_size, NULL, NULL, &items, &item_size); - gtk_icon_view_get_preferred_item_size (icon_view, other_orientation, item_size, &item_min, &item_nat); - *minimum = (item_min + priv->row_spacing) * ((n_items + items - 1) / items) - priv->row_spacing; - *natural = (item_nat + priv->row_spacing) * ((n_items + items - 1) / items) - priv->row_spacing; - } - - *minimum += 2 * priv->margin; - *natural += 2 * priv->margin; -} - - -static void -gtk_icon_view_allocate_children (GtkIconView *icon_view) -{ - GList *list; - - for (list = icon_view->priv->children; list; list = list->next) - { - GtkIconViewChild *child = list->data; - - /* totally ignore our child's requisition */ - gtk_widget_size_allocate (child->widget, &child->area, -1); - } -} - -static void -gtk_icon_view_size_allocate (GtkWidget *widget, - int width, - int height, - int baseline) -{ - GtkIconView *icon_view = GTK_ICON_VIEW (widget); - - gtk_icon_view_layout (icon_view); - - gtk_icon_view_allocate_children (icon_view); - - /* Delay signal emission */ - g_object_freeze_notify (G_OBJECT (icon_view->priv->hadjustment)); - g_object_freeze_notify (G_OBJECT (icon_view->priv->vadjustment)); - - gtk_icon_view_set_hadjustment_values (icon_view); - gtk_icon_view_set_vadjustment_values (icon_view); - - if (gtk_widget_get_realized (widget) && - icon_view->priv->scroll_to_path) - { - GtkTreePath *path; - path = gtk_tree_row_reference_get_path (icon_view->priv->scroll_to_path); - gtk_tree_row_reference_free (icon_view->priv->scroll_to_path); - icon_view->priv->scroll_to_path = NULL; - - gtk_icon_view_scroll_to_path (icon_view, path, - icon_view->priv->scroll_to_use_align, - icon_view->priv->scroll_to_row_align, - icon_view->priv->scroll_to_col_align); - gtk_tree_path_free (path); - } - - /* Emit any pending signals now */ - g_object_thaw_notify (G_OBJECT (icon_view->priv->hadjustment)); - g_object_thaw_notify (G_OBJECT (icon_view->priv->vadjustment)); -} - -static void -gtk_icon_view_snapshot (GtkWidget *widget, - GtkSnapshot *snapshot) -{ - GtkIconView *icon_view; - GList *icons; - GtkTreePath *path; - int dest_index; - GtkIconViewDropPosition dest_pos; - GtkIconViewItem *dest_item = NULL; - GtkStyleContext *context; - int width, height; - double offset_x, offset_y; - - icon_view = GTK_ICON_VIEW (widget); - - context = gtk_widget_get_style_context (widget); - width = gtk_widget_get_width (widget); - height = gtk_widget_get_height (widget); - - offset_x = gtk_adjustment_get_value (icon_view->priv->hadjustment); - offset_y = gtk_adjustment_get_value (icon_view->priv->vadjustment); - - gtk_snapshot_save (snapshot); - gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (- offset_x, - offset_y)); - - gtk_icon_view_get_drag_dest_item (icon_view, &path, &dest_pos); - - if (path) - { - dest_index = gtk_tree_path_get_indices (path)[0]; - gtk_tree_path_free (path); - } - else - dest_index = -1; - - for (icons = icon_view->priv->items; icons; icons = icons->next) - { - GtkIconViewItem *item = icons->data; - graphene_rect_t area; - - graphene_rect_init (&area, - item->cell_area.x - icon_view->priv->item_padding, - item->cell_area.y - icon_view->priv->item_padding, - item->cell_area.width + icon_view->priv->item_padding * 2, - item->cell_area.height + icon_view->priv->item_padding * 2); - - if (gdk_rectangle_intersect (&item->cell_area, - &(GdkRectangle) { offset_x, offset_y, width, height }, NULL)) - { - gtk_icon_view_snapshot_item (icon_view, snapshot, item, - item->cell_area.x, item->cell_area.y, - icon_view->priv->draw_focus); - - if (dest_index == item->index) - dest_item = item; - } - } - - if (dest_item && - dest_pos != GTK_ICON_VIEW_NO_DROP) - { - GdkRectangle rect = { 0 }; - - switch (dest_pos) - { - case GTK_ICON_VIEW_DROP_INTO: - rect = dest_item->cell_area; - break; - case GTK_ICON_VIEW_DROP_ABOVE: - rect.x = dest_item->cell_area.x; - rect.y = dest_item->cell_area.y - 1; - rect.width = dest_item->cell_area.width; - rect.height = 2; - break; - case GTK_ICON_VIEW_DROP_LEFT: - rect.x = dest_item->cell_area.x - 1; - rect.y = dest_item->cell_area.y; - rect.width = 2; - rect.height = dest_item->cell_area.height; - break; - case GTK_ICON_VIEW_DROP_BELOW: - rect.x = dest_item->cell_area.x; - rect.y = dest_item->cell_area.y + dest_item->cell_area.height - 1; - rect.width = dest_item->cell_area.width; - rect.height = 2; - break; - case GTK_ICON_VIEW_DROP_RIGHT: - rect.x = dest_item->cell_area.x + dest_item->cell_area.width - 1; - rect.y = dest_item->cell_area.y; - rect.width = 2; - rect.height = dest_item->cell_area.height; - break; - case GTK_ICON_VIEW_NO_DROP: - default: - break; - } - - gtk_style_context_save_to_node (context, icon_view->priv->dndnode); - gtk_style_context_set_state (context, gtk_style_context_get_state (context) | GTK_STATE_FLAG_DROP_ACTIVE); - - gtk_snapshot_render_frame (snapshot, context, - rect.x, rect.y, - rect.width, rect.height); - - gtk_style_context_restore (context); - } - - if (icon_view->priv->doing_rubberband) - gtk_icon_view_snapshot_rubberband (icon_view, snapshot); - - gtk_snapshot_restore (snapshot); - - GTK_WIDGET_CLASS (gtk_icon_view_parent_class)->snapshot (widget, snapshot); -} - -static gboolean -rubberband_scroll_timeout (gpointer data) -{ - GtkIconView *icon_view = data; - - gtk_adjustment_set_value (icon_view->priv->vadjustment, - gtk_adjustment_get_value (icon_view->priv->vadjustment) + - icon_view->priv->scroll_value_diff); - - gtk_icon_view_update_rubberband (icon_view); - - return TRUE; -} - -static GtkIconViewItem * -_gtk_icon_view_get_item_at_widget_coords (GtkIconView *icon_view, - int x, - int y, - gboolean only_in_cell, - GtkCellRenderer **cell_at_pos) -{ - x += gtk_adjustment_get_value (icon_view->priv->hadjustment); - y += gtk_adjustment_get_value (icon_view->priv->vadjustment); - - return _gtk_icon_view_get_item_at_coords (icon_view, x, y, - only_in_cell, cell_at_pos); -} - -static void -gtk_icon_view_motion (GtkEventController *controller, - double x, - double y, - gpointer user_data) -{ - GtkIconView *icon_view; - int abs_y; - GdkDevice *device; - - icon_view = GTK_ICON_VIEW (user_data); - - icon_view->priv->mouse_x = x; - icon_view->priv->mouse_y = y; - - device = gtk_event_controller_get_current_event_device (controller); - gtk_icon_view_maybe_begin_drag (icon_view, x, y, device); - - if (icon_view->priv->doing_rubberband) - { - int height; - gtk_icon_view_update_rubberband (icon_view); - - abs_y = icon_view->priv->mouse_y - icon_view->priv->height * - (gtk_adjustment_get_value (icon_view->priv->vadjustment) / - (gtk_adjustment_get_upper (icon_view->priv->vadjustment) - - gtk_adjustment_get_lower (icon_view->priv->vadjustment))); - - height = gtk_widget_get_height (GTK_WIDGET (icon_view)); - - - if (abs_y < 0 || abs_y > height) - { - if (abs_y < 0) - icon_view->priv->scroll_value_diff = abs_y; - else - icon_view->priv->scroll_value_diff = abs_y - height; - - icon_view->priv->event_last_x = icon_view->priv->mouse_x; - icon_view->priv->event_last_y = icon_view->priv->mouse_x; - - if (icon_view->priv->scroll_timeout_id == 0) { - icon_view->priv->scroll_timeout_id = g_timeout_add (30, rubberband_scroll_timeout, icon_view); - gdk_source_set_static_name_by_id (icon_view->priv->scroll_timeout_id, "[gtk] rubberband_scroll_timeout"); - } - } - else - remove_scroll_timeout (icon_view); - } - else - { - GtkIconViewItem *item, *last_prelight_item; - GtkCellRenderer *cell = NULL; - - last_prelight_item = icon_view->priv->last_prelight; - item = _gtk_icon_view_get_item_at_widget_coords (icon_view, - icon_view->priv->mouse_x, - icon_view->priv->mouse_y, - FALSE, - &cell); - - if (item != last_prelight_item) - { - if (item != NULL) - { - gtk_icon_view_queue_draw_item (icon_view, item); - } - - if (last_prelight_item != NULL) - { - gtk_icon_view_queue_draw_item (icon_view, - icon_view->priv->last_prelight); - } - - icon_view->priv->last_prelight = item; - } - } -} - -static void -gtk_icon_view_leave (GtkEventController *controller, - gpointer user_data) -{ - GtkIconView *icon_view; - GtkIconViewPrivate *priv; - - icon_view = GTK_ICON_VIEW (user_data); - priv = icon_view->priv; - - if (priv->last_prelight) - { - gtk_icon_view_queue_draw_item (icon_view, priv->last_prelight); - priv->last_prelight = NULL; - } -} - -static void -gtk_icon_view_remove (GtkIconView *icon_view, - GtkWidget *widget) -{ - GtkIconViewChild *child = NULL; - GList *tmp_list; - - tmp_list = icon_view->priv->children; - while (tmp_list) - { - child = tmp_list->data; - if (child->widget == widget) - { - gtk_widget_unparent (widget); - - icon_view->priv->children = g_list_remove_link (icon_view->priv->children, tmp_list); - g_list_free_1 (tmp_list); - g_free (child); - return; - } - - tmp_list = tmp_list->next; - } -} - -static void -gtk_icon_view_add_editable (GtkCellArea *area, - GtkCellRenderer *renderer, - GtkCellEditable *editable, - GdkRectangle *cell_area, - const char *path, - GtkIconView *icon_view) -{ - GtkIconViewChild *child; - GtkWidget *widget = GTK_WIDGET (editable); - - child = g_new (GtkIconViewChild, 1); - - child->widget = widget; - child->area.x = cell_area->x; - child->area.y = cell_area->y; - child->area.width = cell_area->width; - child->area.height = cell_area->height; - - icon_view->priv->children = g_list_append (icon_view->priv->children, child); - - gtk_widget_set_parent (widget, GTK_WIDGET (icon_view)); -} - -static void -gtk_icon_view_remove_editable (GtkCellArea *area, - GtkCellRenderer *renderer, - GtkCellEditable *editable, - GtkIconView *icon_view) -{ - GtkTreePath *path; - - if (gtk_widget_has_focus (GTK_WIDGET (editable))) - gtk_widget_grab_focus (GTK_WIDGET (icon_view)); - - gtk_icon_view_remove (icon_view, GTK_WIDGET (editable)); - - path = gtk_tree_path_new_from_string (gtk_cell_area_get_current_path_string (area)); - gtk_icon_view_queue_draw_path (icon_view, path); - gtk_tree_path_free (path); -} - -/** - * gtk_icon_view_set_cursor: - * @icon_view: A `GtkIconView` - * @path: A `GtkTreePath` - * @cell: (nullable): One of the cell renderers of @icon_view - * @start_editing: %TRUE if the specified cell should start being edited. - * - * Sets the current keyboard focus to be at @path, and selects it. This is - * useful when you want to focus the user’s attention on a particular item. - * If @cell is not %NULL, then focus is given to the cell specified by - * it. Additionally, if @start_editing is %TRUE, then editing should be - * started in the specified cell. - * - * This function is often followed by `gtk_widget_grab_focus - * (icon_view)` in order to give keyboard focus to the widget. - * Please note that editing can only happen when the widget is realized. - **/ -void -gtk_icon_view_set_cursor (GtkIconView *icon_view, - GtkTreePath *path, - GtkCellRenderer *cell, - gboolean start_editing) -{ - GtkIconViewItem *item = NULL; - - g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); - g_return_if_fail (path != NULL); - g_return_if_fail (cell == NULL || GTK_IS_CELL_RENDERER (cell)); - - if (icon_view->priv->cell_area) - gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); - - if (gtk_tree_path_get_depth (path) == 1) - item = g_list_nth_data (icon_view->priv->items, - gtk_tree_path_get_indices(path)[0]); - - if (!item) - return; - - _gtk_icon_view_set_cursor_item (icon_view, item, cell); - gtk_icon_view_scroll_to_path (icon_view, path, FALSE, 0.0, 0.0); - - if (start_editing && - icon_view->priv->cell_area) - { - GtkCellAreaContext *context; - - context = g_ptr_array_index (icon_view->priv->row_contexts, item->row); - _gtk_icon_view_set_cell_data (icon_view, item); - gtk_cell_area_activate (icon_view->priv->cell_area, context, - GTK_WIDGET (icon_view), &item->cell_area, - 0, TRUE); - } -} - -/** - * gtk_icon_view_get_cursor: - * @icon_view: A `GtkIconView` - * @path: (out) (optional) (transfer full): Return location for the current - * cursor path - * @cell: (out) (optional) (transfer none): Return location the current - * focus cell - * - * Fills in @path and @cell with the current cursor path and cell. - * If the cursor isn’t currently set, then *@path will be %NULL. - * If no cell currently has focus, then *@cell will be %NULL. - * - * The returned `GtkTreePath` must be freed with gtk_tree_path_free(). - * - * Returns: %TRUE if the cursor is set. - **/ -gboolean -gtk_icon_view_get_cursor (GtkIconView *icon_view, - GtkTreePath **path, - GtkCellRenderer **cell) -{ - GtkIconViewItem *item; - - g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), FALSE); - - item = icon_view->priv->cursor_item; - - if (path != NULL) - { - if (item != NULL) - *path = gtk_tree_path_new_from_indices (item->index, -1); - else - *path = NULL; - } - - if (cell != NULL && item != NULL && icon_view->priv->cell_area != NULL) - *cell = gtk_cell_area_get_focus_cell (icon_view->priv->cell_area); - - return (item != NULL); -} - -static void -gtk_icon_view_button_press (GtkGestureClick *gesture, - int n_press, - double x, - double y, - gpointer user_data) -{ - GtkIconView *icon_view = user_data; - GtkWidget *widget = GTK_WIDGET (icon_view); - GtkIconViewItem *item; - gboolean dirty = FALSE; - GtkCellRenderer *cell = NULL, *cursor_cell = NULL; - int button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture)); - GdkEventSequence *sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture)); - GdkEvent *event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence); - GdkModifierType state; - - if (!gtk_widget_has_focus (widget)) - gtk_widget_grab_focus (widget); - - if (button == GDK_BUTTON_PRIMARY) - { - GdkModifierType extend_mod_mask = GDK_SHIFT_MASK; - GdkModifierType modify_mod_mask = GDK_CONTROL_MASK; - - state = gdk_event_get_modifier_state (event); - - item = _gtk_icon_view_get_item_at_widget_coords (icon_view, - x, y, - FALSE, - &cell); - - /* - * We consider only the cells' area as the item area if the - * item is not selected, but if it *is* selected, the complete - * selection rectangle is considered to be part of the item. - */ - if (item != NULL && (cell != NULL || item->selected)) - { - if (cell != NULL) - { - if (gtk_cell_renderer_is_activatable (cell)) - cursor_cell = cell; - } - - gtk_icon_view_scroll_to_item (icon_view, item); - - if (icon_view->priv->selection_mode == GTK_SELECTION_NONE) - { - _gtk_icon_view_set_cursor_item (icon_view, item, cursor_cell); - } - else if (icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE && - (state & extend_mod_mask)) - { - gtk_icon_view_unselect_all_internal (icon_view); - - _gtk_icon_view_set_cursor_item (icon_view, item, cursor_cell); - if (!icon_view->priv->anchor_item) - icon_view->priv->anchor_item = item; - else - gtk_icon_view_select_all_between (icon_view, - icon_view->priv->anchor_item, - item); - dirty = TRUE; - } - else - { - if ((icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE || - ((icon_view->priv->selection_mode == GTK_SELECTION_SINGLE) && item->selected)) && - (state & modify_mod_mask)) - { - item->selected = !item->selected; - gtk_icon_view_queue_draw_item (icon_view, item); - dirty = TRUE; - } - else - { - gtk_icon_view_unselect_all_internal (icon_view); - - item->selected = TRUE; - gtk_icon_view_queue_draw_item (icon_view, item); - dirty = TRUE; - } - _gtk_icon_view_set_cursor_item (icon_view, item, cursor_cell); - icon_view->priv->anchor_item = item; - } - - /* Save press to possibly begin a drag */ - if (icon_view->priv->pressed_button < 0) - { - icon_view->priv->pressed_button = button; - icon_view->priv->press_start_x = x; - icon_view->priv->press_start_y = y; - } - - icon_view->priv->last_single_clicked = item; - - /* cancel the current editing, if it exists */ - gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); - - if (cell != NULL && gtk_cell_renderer_is_activatable (cell)) - { - GtkCellAreaContext *context; - - context = g_ptr_array_index (icon_view->priv->row_contexts, item->row); - - _gtk_icon_view_set_cell_data (icon_view, item); - gtk_cell_area_activate (icon_view->priv->cell_area, context, - GTK_WIDGET (icon_view), - &item->cell_area, 0, FALSE); - } - } - else - { - if (icon_view->priv->selection_mode != GTK_SELECTION_BROWSE && - !(state & modify_mod_mask)) - { - dirty = gtk_icon_view_unselect_all_internal (icon_view); - } - - if (icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE) - gtk_icon_view_start_rubberbanding (icon_view, - gtk_gesture_get_device (GTK_GESTURE (gesture)), - x, y); - } - - /* don't draw keyboard focus around a clicked-on item */ - icon_view->priv->draw_focus = FALSE; - } - - if (!icon_view->priv->activate_on_single_click - && button == GDK_BUTTON_PRIMARY - && n_press == 2) - { - item = _gtk_icon_view_get_item_at_widget_coords (icon_view, - x, y, - FALSE, - NULL); - - if (item && item == icon_view->priv->last_single_clicked) - { - GtkTreePath *path; - - path = gtk_tree_path_new_from_indices (item->index, -1); - gtk_icon_view_item_activated (icon_view, path); - gtk_tree_path_free (path); - } - - icon_view->priv->last_single_clicked = NULL; - icon_view->priv->pressed_button = -1; - } - - if (dirty) - g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0); -} - -static gboolean -button_event_modifies_selection (GdkEvent *event) -{ - guint state = gdk_event_get_modifier_state (event); - - return (state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) != 0; -} - -static void -gtk_icon_view_button_release (GtkGestureClick *gesture, - int n_press, - double x, - double y, - gpointer user_data) -{ - GtkIconView *icon_view = user_data; - int button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture)); - GdkEventSequence *sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture)); - GdkEvent *event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence); - - if (icon_view->priv->pressed_button == button) - icon_view->priv->pressed_button = -1; - - gtk_icon_view_stop_rubberbanding (icon_view); - - remove_scroll_timeout (icon_view); - - if (button == GDK_BUTTON_PRIMARY - && icon_view->priv->activate_on_single_click - && !button_event_modifies_selection (event) - && icon_view->priv->last_single_clicked != NULL) - { - GtkIconViewItem *item; - - item = _gtk_icon_view_get_item_at_widget_coords (icon_view, - x, y, - FALSE, - NULL); - if (item == icon_view->priv->last_single_clicked) - { - GtkTreePath *path; - path = gtk_tree_path_new_from_indices (item->index, -1); - gtk_icon_view_item_activated (icon_view, path); - gtk_tree_path_free (path); - } - - icon_view->priv->last_single_clicked = NULL; - } -} - -static gboolean -gtk_icon_view_key_pressed (GtkEventControllerKey *controller, - guint keyval, - guint keycode, - GdkModifierType state, - GtkWidget *widget) -{ - GtkIconView *icon_view = GTK_ICON_VIEW (widget); - - if (icon_view->priv->doing_rubberband) - { - if (keyval == GDK_KEY_Escape) - gtk_icon_view_stop_rubberbanding (icon_view); - - return TRUE; - } - - return FALSE; -} - -static void -gtk_icon_view_update_rubberband (GtkIconView *icon_view) -{ - int x, y; - - x = MAX (icon_view->priv->mouse_x, 0); - y = MAX (icon_view->priv->mouse_y, 0); - - icon_view->priv->rubberband_x2 = x + gtk_adjustment_get_value (icon_view->priv->hadjustment); - icon_view->priv->rubberband_y2 = y + gtk_adjustment_get_value (icon_view->priv->vadjustment); - - gtk_icon_view_update_rubberband_selection (icon_view); - gtk_widget_queue_draw (GTK_WIDGET (icon_view)); -} - -static void -gtk_icon_view_start_rubberbanding (GtkIconView *icon_view, - GdkDevice *device, - int x, - int y) -{ - GtkIconViewPrivate *priv = icon_view->priv; - GList *items; - GtkCssNode *widget_node; - - if (priv->rubberband_device) - return; - - for (items = priv->items; items; items = items->next) - { - GtkIconViewItem *item = items->data; - - item->selected_before_rubberbanding = item->selected; - } - - priv->rubberband_x1 = x + gtk_adjustment_get_value (priv->hadjustment); - priv->rubberband_y1 = y + gtk_adjustment_get_value (priv->vadjustment); - priv->rubberband_x2 = priv->rubberband_x1; - priv->rubberband_y2 = priv->rubberband_y1; - - priv->doing_rubberband = TRUE; - priv->rubberband_device = device; - - widget_node = gtk_widget_get_css_node (GTK_WIDGET (icon_view)); - priv->rubberband_node = gtk_css_node_new (); - gtk_css_node_set_name (priv->rubberband_node, g_quark_from_static_string ("rubberband")); - gtk_css_node_set_parent (priv->rubberband_node, widget_node); - gtk_css_node_set_state (priv->rubberband_node, gtk_css_node_get_state (widget_node)); - g_object_unref (priv->rubberband_node); -} - -static void -gtk_icon_view_stop_rubberbanding (GtkIconView *icon_view) -{ - GtkIconViewPrivate *priv = icon_view->priv; - - if (!priv->doing_rubberband) - return; - - priv->doing_rubberband = FALSE; - priv->rubberband_device = NULL; - gtk_css_node_set_parent (priv->rubberband_node, NULL); - priv->rubberband_node = NULL; - - gtk_widget_queue_draw (GTK_WIDGET (icon_view)); -} - -static void -gtk_icon_view_update_rubberband_selection (GtkIconView *icon_view) -{ - GList *items; - int x, y, width, height; - gboolean dirty = FALSE; - - x = MIN (icon_view->priv->rubberband_x1, - icon_view->priv->rubberband_x2); - y = MIN (icon_view->priv->rubberband_y1, - icon_view->priv->rubberband_y2); - width = ABS (icon_view->priv->rubberband_x1 - - icon_view->priv->rubberband_x2); - height = ABS (icon_view->priv->rubberband_y1 - - icon_view->priv->rubberband_y2); - - for (items = icon_view->priv->items; items; items = items->next) - { - GtkIconViewItem *item = items->data; - gboolean is_in; - gboolean selected; - - is_in = gtk_icon_view_item_hit_test (icon_view, item, - x, y, width, height); - - selected = is_in ^ item->selected_before_rubberbanding; - - if (item->selected != selected) - { - item->selected = selected; - dirty = TRUE; - gtk_icon_view_queue_draw_item (icon_view, item); - } - } - - if (dirty) - g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0); -} - - -typedef struct { - GdkRectangle hit_rect; - gboolean hit; -} HitTestData; - -static gboolean -hit_test (GtkCellRenderer *renderer, - const GdkRectangle *cell_area, - const GdkRectangle *cell_background, - HitTestData *data) -{ - if (MIN (data->hit_rect.x + data->hit_rect.width, cell_area->x + cell_area->width) - - MAX (data->hit_rect.x, cell_area->x) > 0 && - MIN (data->hit_rect.y + data->hit_rect.height, cell_area->y + cell_area->height) - - MAX (data->hit_rect.y, cell_area->y) > 0) - data->hit = TRUE; - - return (data->hit != FALSE); -} - -static gboolean -gtk_icon_view_item_hit_test (GtkIconView *icon_view, - GtkIconViewItem *item, - int x, - int y, - int width, - int height) -{ - HitTestData data = { { x, y, width, height }, FALSE }; - GtkCellAreaContext *context; - GdkRectangle *item_area = &item->cell_area; - - if (MIN (x + width, item_area->x + item_area->width) - MAX (x, item_area->x) <= 0 || - MIN (y + height, item_area->y + item_area->height) - MAX (y, item_area->y) <= 0) - return FALSE; - - context = g_ptr_array_index (icon_view->priv->row_contexts, item->row); - - _gtk_icon_view_set_cell_data (icon_view, item); - gtk_cell_area_foreach_alloc (icon_view->priv->cell_area, context, - GTK_WIDGET (icon_view), - item_area, item_area, - (GtkCellAllocCallback)hit_test, &data); - - return data.hit; -} - -static gboolean -gtk_icon_view_unselect_all_internal (GtkIconView *icon_view) -{ - gboolean dirty = FALSE; - GList *items; - - if (icon_view->priv->selection_mode == GTK_SELECTION_NONE) - return FALSE; - - for (items = icon_view->priv->items; items; items = items->next) - { - GtkIconViewItem *item = items->data; - - if (item->selected) - { - item->selected = FALSE; - dirty = TRUE; - gtk_icon_view_queue_draw_item (icon_view, item); - } - } - - return dirty; -} - - -/* GtkIconView signals */ -static void -gtk_icon_view_real_select_all (GtkIconView *icon_view) -{ - gtk_icon_view_select_all (icon_view); -} - -static void -gtk_icon_view_real_unselect_all (GtkIconView *icon_view) -{ - gtk_icon_view_unselect_all (icon_view); -} - -static void -gtk_icon_view_real_select_cursor_item (GtkIconView *icon_view) -{ - gtk_icon_view_unselect_all (icon_view); - - if (icon_view->priv->cursor_item != NULL) - _gtk_icon_view_select_item (icon_view, icon_view->priv->cursor_item); -} - -static gboolean -gtk_icon_view_real_activate_cursor_item (GtkIconView *icon_view) -{ - GtkTreePath *path; - GtkCellAreaContext *context; - - if (!icon_view->priv->cursor_item) - return FALSE; - - context = g_ptr_array_index (icon_view->priv->row_contexts, icon_view->priv->cursor_item->row); - - _gtk_icon_view_set_cell_data (icon_view, icon_view->priv->cursor_item); - gtk_cell_area_activate (icon_view->priv->cell_area, context, - GTK_WIDGET (icon_view), - &icon_view->priv->cursor_item->cell_area, - 0, - FALSE); - - path = gtk_tree_path_new_from_indices (icon_view->priv->cursor_item->index, -1); - gtk_icon_view_item_activated (icon_view, path); - gtk_tree_path_free (path); - - return TRUE; -} - -static void -gtk_icon_view_real_toggle_cursor_item (GtkIconView *icon_view) -{ - if (!icon_view->priv->cursor_item) - return; - - switch (icon_view->priv->selection_mode) - { - case GTK_SELECTION_NONE: - default: - break; - case GTK_SELECTION_BROWSE: - _gtk_icon_view_select_item (icon_view, icon_view->priv->cursor_item); - break; - case GTK_SELECTION_SINGLE: - if (icon_view->priv->cursor_item->selected) - _gtk_icon_view_unselect_item (icon_view, icon_view->priv->cursor_item); - else - _gtk_icon_view_select_item (icon_view, icon_view->priv->cursor_item); - break; - case GTK_SELECTION_MULTIPLE: - icon_view->priv->cursor_item->selected = !icon_view->priv->cursor_item->selected; - g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0); - - gtk_icon_view_queue_draw_item (icon_view, icon_view->priv->cursor_item); - break; - } -} - -static void -gtk_icon_view_set_hadjustment_values (GtkIconView *icon_view) -{ - int width; - GtkAdjustment *adj = icon_view->priv->hadjustment; - double old_page_size; - double old_upper; - double old_value; - double new_value; - double new_upper; - - width = gtk_widget_get_width (GTK_WIDGET (icon_view)); - - old_value = gtk_adjustment_get_value (adj); - old_upper = gtk_adjustment_get_upper (adj); - old_page_size = gtk_adjustment_get_page_size (adj); - new_upper = MAX (width, icon_view->priv->width); - - if (gtk_widget_get_direction (GTK_WIDGET (icon_view)) == GTK_TEXT_DIR_RTL) - { - /* Make sure no scrolling occurs for RTL locales also (if possible) */ - /* Quick explanation: - * In LTR locales, leftmost portion of visible rectangle should stay - * fixed, which means left edge of scrollbar thumb should remain fixed - * and thus adjustment's value should stay the same. - * - * In RTL locales, we want to keep rightmost portion of visible - * rectangle fixed. This means right edge of thumb should remain fixed. - * In this case, upper - value - page_size should remain constant. - */ - new_value = (new_upper - width) - - (old_upper - old_value - old_page_size); - new_value = CLAMP (new_value, 0, new_upper - width); - } - else - new_value = CLAMP (old_value, 0, new_upper - width); - - gtk_adjustment_configure (adj, - new_value, - 0.0, - new_upper, - width * 0.1, - width * 0.9, - width); -} - -static void -gtk_icon_view_set_vadjustment_values (GtkIconView *icon_view) -{ - int height; - GtkAdjustment *adj = icon_view->priv->vadjustment; - - height = gtk_widget_get_height (GTK_WIDGET (icon_view)); - - gtk_adjustment_configure (adj, - gtk_adjustment_get_value (adj), - 0.0, - MAX (height, icon_view->priv->height), - height * 0.1, - height * 0.9, - height); -} - -static void -gtk_icon_view_set_hadjustment (GtkIconView *icon_view, - GtkAdjustment *adjustment) -{ - GtkIconViewPrivate *priv = icon_view->priv; - - if (adjustment && priv->hadjustment == adjustment) - return; - - if (priv->hadjustment != NULL) - { - g_signal_handlers_disconnect_matched (priv->hadjustment, - G_SIGNAL_MATCH_DATA, - 0, 0, NULL, NULL, icon_view); - g_object_unref (priv->hadjustment); - } - - if (!adjustment) - adjustment = gtk_adjustment_new (0.0, 0.0, 0.0, - 0.0, 0.0, 0.0); - - g_signal_connect (adjustment, "value-changed", - G_CALLBACK (gtk_icon_view_adjustment_changed), icon_view); - priv->hadjustment = g_object_ref_sink (adjustment); - gtk_icon_view_set_hadjustment_values (icon_view); - - g_object_notify (G_OBJECT (icon_view), "hadjustment"); -} - -static void -gtk_icon_view_set_vadjustment (GtkIconView *icon_view, - GtkAdjustment *adjustment) -{ - GtkIconViewPrivate *priv = icon_view->priv; - - if (adjustment && priv->vadjustment == adjustment) - return; - - if (priv->vadjustment != NULL) - { - g_signal_handlers_disconnect_matched (priv->vadjustment, - G_SIGNAL_MATCH_DATA, - 0, 0, NULL, NULL, icon_view); - g_object_unref (priv->vadjustment); - } - - if (!adjustment) - adjustment = gtk_adjustment_new (0.0, 0.0, 0.0, - 0.0, 0.0, 0.0); - - g_signal_connect (adjustment, "value-changed", - G_CALLBACK (gtk_icon_view_adjustment_changed), icon_view); - priv->vadjustment = g_object_ref_sink (adjustment); - gtk_icon_view_set_vadjustment_values (icon_view); - - g_object_notify (G_OBJECT (icon_view), "vadjustment"); -} - -static void -gtk_icon_view_adjustment_changed (GtkAdjustment *adjustment, - GtkIconView *icon_view) -{ - GtkWidget *widget = GTK_WIDGET (icon_view); - - if (gtk_widget_get_realized (widget)) - { - if (icon_view->priv->doing_rubberband) - gtk_icon_view_update_rubberband (icon_view); - } - - gtk_widget_queue_draw (GTK_WIDGET (icon_view)); -} - -static int -compare_sizes (gconstpointer p1, - gconstpointer p2, - gpointer unused) -{ - return GPOINTER_TO_INT (((const GtkRequestedSize *) p1)->data) - - GPOINTER_TO_INT (((const GtkRequestedSize *) p2)->data); -} - -static void -gtk_icon_view_layout (GtkIconView *icon_view) -{ - GtkIconViewPrivate *priv = icon_view->priv; - GtkWidget *widget = GTK_WIDGET (icon_view); - GList *items; - int item_width = 0; /* this doesn't include item_padding */ - int n_columns, n_rows, n_items; - int col, row; - GtkRequestedSize *sizes; - gboolean rtl; - int width, height; - - if (gtk_icon_view_is_empty (icon_view)) - return; - - rtl = gtk_widget_get_direction (GTK_WIDGET (icon_view)) == GTK_TEXT_DIR_RTL; - n_items = gtk_icon_view_get_n_items (icon_view); - - width = gtk_widget_get_width (widget); - height = gtk_widget_get_height (widget); - - gtk_icon_view_compute_n_items_for_size (icon_view, - GTK_ORIENTATION_HORIZONTAL, - width, - NULL, NULL, - &n_columns, &item_width); - n_rows = (n_items + n_columns - 1) / n_columns; - - priv->width = n_columns * (item_width + 2 * priv->item_padding + priv->column_spacing) - priv->column_spacing; - priv->width += 2 * priv->margin; - priv->width = MAX (priv->width, width); - - /* Clear the per row contexts */ - g_ptr_array_set_size (icon_view->priv->row_contexts, 0); - - gtk_cell_area_context_reset (priv->cell_area_context); - /* because layouting is complicated. We designed an API - * that is O(N²) and nonsensical. - * And we're proud of it. */ - for (items = priv->items; items; items = items->next) - { - _gtk_icon_view_set_cell_data (icon_view, items->data); - gtk_cell_area_get_preferred_width (priv->cell_area, - priv->cell_area_context, - widget, - NULL, NULL); - } - - sizes = g_newa (GtkRequestedSize, n_rows); - items = priv->items; - priv->height = priv->margin; - - /* Collect the heights for all rows */ - for (row = 0; row < n_rows; row++) - { - GtkCellAreaContext *context = gtk_cell_area_copy_context (priv->cell_area, priv->cell_area_context); - g_ptr_array_add (priv->row_contexts, context); - - for (col = 0; col < n_columns && items; col++, items = items->next) - { - GtkIconViewItem *item = items->data; - - _gtk_icon_view_set_cell_data (icon_view, item); - gtk_cell_area_get_preferred_height_for_width (priv->cell_area, - context, - widget, - item_width, - NULL, NULL); - } - - sizes[row].data = GINT_TO_POINTER (row); - gtk_cell_area_context_get_preferred_height_for_width (context, - item_width, - &sizes[row].minimum_size, - &sizes[row].natural_size); - priv->height += sizes[row].minimum_size + 2 * priv->item_padding + priv->row_spacing; - } - - priv->height -= priv->row_spacing; - priv->height += priv->margin; - priv->height = MIN (priv->height, height); - - gtk_distribute_natural_allocation (height - priv->height, - n_rows, - sizes); - - /* Actually allocate the rows */ - g_qsort_with_data (sizes, n_rows, sizeof (GtkRequestedSize), compare_sizes, NULL); - - items = priv->items; - priv->height = priv->margin; - - for (row = 0; row < n_rows; row++) - { - GtkCellAreaContext *context = g_ptr_array_index (priv->row_contexts, row); - gtk_cell_area_context_allocate (context, item_width, sizes[row].minimum_size); - - priv->height += priv->item_padding; - - for (col = 0; col < n_columns && items; col++, items = items->next) - { - GtkIconViewItem *item = items->data; - - item->cell_area.x = priv->margin + (col * 2 + 1) * priv->item_padding + col * (priv->column_spacing + item_width); - item->cell_area.width = item_width; - item->cell_area.y = priv->height; - item->cell_area.height = sizes[row].minimum_size; - item->row = row; - item->col = col; - if (rtl) - { - item->cell_area.x = priv->width - item_width - item->cell_area.x; - item->col = n_columns - 1 - col; - } - } - - priv->height += sizes[row].minimum_size + priv->item_padding + priv->row_spacing; - } - - priv->height -= priv->row_spacing; - priv->height += priv->margin; - priv->height = MAX (priv->height, height); -} - -static void -gtk_icon_view_invalidate_sizes (GtkIconView *icon_view) -{ - /* Clear all item sizes */ - g_list_foreach (icon_view->priv->items, - (GFunc)gtk_icon_view_item_invalidate_size, NULL); - - /* Re-layout the items */ - gtk_widget_queue_resize (GTK_WIDGET (icon_view)); -} - -static void -gtk_icon_view_item_invalidate_size (GtkIconViewItem *item) -{ - item->cell_area.width = -1; - item->cell_area.height = -1; -} - -static void -gtk_icon_view_snapshot_item (GtkIconView *icon_view, - GtkSnapshot *snapshot, - GtkIconViewItem *item, - int x, - int y, - gboolean draw_focus) -{ - GdkRectangle cell_area; - GtkStateFlags state = 0; - GtkCellRendererState flags = 0; - GtkStyleContext *style_context; - GtkWidget *widget = GTK_WIDGET (icon_view); - GtkIconViewPrivate *priv = icon_view->priv; - GtkCellAreaContext *context; - - if (priv->model == NULL || item->cell_area.width <= 0 || item->cell_area.height <= 0) - return; - - _gtk_icon_view_set_cell_data (icon_view, item); - - style_context = gtk_widget_get_style_context (widget); - state = gtk_widget_get_state_flags (widget); - - gtk_style_context_save (style_context); - gtk_style_context_add_class (style_context, "cell"); - - state &= ~(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_PRELIGHT); - - if ((state & GTK_STATE_FLAG_FOCUSED) && - item == icon_view->priv->cursor_item) - flags |= GTK_CELL_RENDERER_FOCUSED; - - if (item->selected) - { - state |= GTK_STATE_FLAG_SELECTED; - flags |= GTK_CELL_RENDERER_SELECTED; - } - - if (item == priv->last_prelight) - { - state |= GTK_STATE_FLAG_PRELIGHT; - flags |= GTK_CELL_RENDERER_PRELIT; - } - - gtk_style_context_set_state (style_context, state); - - gtk_snapshot_render_background (snapshot, style_context, - x - priv->item_padding, - y - priv->item_padding, - item->cell_area.width + priv->item_padding * 2, - item->cell_area.height + priv->item_padding * 2); - gtk_snapshot_render_frame (snapshot, style_context, - x - priv->item_padding, - y - priv->item_padding, - item->cell_area.width + priv->item_padding * 2, - item->cell_area.height + priv->item_padding * 2); - - cell_area.x = x; - cell_area.y = y; - cell_area.width = item->cell_area.width; - cell_area.height = item->cell_area.height; - - context = g_ptr_array_index (priv->row_contexts, item->row); - gtk_cell_area_snapshot (priv->cell_area, context, - widget, snapshot, &cell_area, &cell_area, flags, - draw_focus); - - gtk_style_context_restore (style_context); -} - -static void -gtk_icon_view_snapshot_rubberband (GtkIconView *icon_view, - GtkSnapshot *snapshot) -{ - GtkIconViewPrivate *priv = icon_view->priv; - GtkStyleContext *context; - GdkRectangle rect; - - rect.x = MIN (priv->rubberband_x1, priv->rubberband_x2); - rect.y = MIN (priv->rubberband_y1, priv->rubberband_y2); - rect.width = ABS (priv->rubberband_x1 - priv->rubberband_x2) + 1; - rect.height = ABS (priv->rubberband_y1 - priv->rubberband_y2) + 1; - - context = gtk_widget_get_style_context (GTK_WIDGET (icon_view)); - - gtk_style_context_save_to_node (context, priv->rubberband_node); - - gtk_snapshot_render_background (snapshot, context, - rect.x, rect.y, - rect.width, rect.height); - gtk_snapshot_render_frame (snapshot, context, - rect.x, rect.y, - rect.width, rect.height); - - gtk_style_context_restore (context); -} - -static void -gtk_icon_view_queue_draw_path (GtkIconView *icon_view, - GtkTreePath *path) -{ - GList *l; - int index; - - index = gtk_tree_path_get_indices (path)[0]; - - for (l = icon_view->priv->items; l; l = l->next) - { - GtkIconViewItem *item = l->data; - - if (item->index == index) - { - gtk_icon_view_queue_draw_item (icon_view, item); - break; - } - } -} - -static void -gtk_icon_view_queue_draw_item (GtkIconView *icon_view, - GtkIconViewItem *item) -{ - gtk_widget_queue_draw (GTK_WIDGET (icon_view)); -} - -void -_gtk_icon_view_set_cursor_item (GtkIconView *icon_view, - GtkIconViewItem *item, - GtkCellRenderer *cursor_cell) -{ - /* When hitting this path from keynav, the focus cell is already set, - * but we still need to queue the draw here (in the case that the focus - * cell changes but not the cursor item). - */ - gtk_icon_view_queue_draw_item (icon_view, item); - - if (icon_view->priv->cursor_item == item && - (cursor_cell == NULL || cursor_cell == gtk_cell_area_get_focus_cell (icon_view->priv->cell_area))) - return; - - if (icon_view->priv->cursor_item != NULL) - gtk_icon_view_queue_draw_item (icon_view, icon_view->priv->cursor_item); - - icon_view->priv->cursor_item = item; - - if (cursor_cell) - gtk_cell_area_set_focus_cell (icon_view->priv->cell_area, cursor_cell); - else - { - /* Make sure there is a cell in focus initially */ - if (!gtk_cell_area_get_focus_cell (icon_view->priv->cell_area)) - gtk_cell_area_focus (icon_view->priv->cell_area, GTK_DIR_TAB_FORWARD); - } -} - - -static GtkIconViewItem * -gtk_icon_view_item_new (void) -{ - GtkIconViewItem *item; - - item = g_slice_new0 (GtkIconViewItem); - - item->cell_area.width = -1; - item->cell_area.height = -1; - - return item; -} - -static void -gtk_icon_view_item_free (GtkIconViewItem *item) -{ - g_return_if_fail (item != NULL); - - g_slice_free (GtkIconViewItem, item); -} - -GtkIconViewItem * -_gtk_icon_view_get_item_at_coords (GtkIconView *icon_view, - int x, - int y, - gboolean only_in_cell, - GtkCellRenderer **cell_at_pos) -{ - GList *items; - - if (cell_at_pos) - *cell_at_pos = NULL; - - for (items = icon_view->priv->items; items; items = items->next) - { - GtkIconViewItem *item = items->data; - GdkRectangle *item_area = &item->cell_area; - - if (x >= item_area->x - icon_view->priv->column_spacing/2 && - x <= item_area->x + item_area->width + icon_view->priv->column_spacing/2 && - y >= item_area->y - icon_view->priv->row_spacing/2 && - y <= item_area->y + item_area->height + icon_view->priv->row_spacing/2) - { - if (only_in_cell || cell_at_pos) - { - GtkCellRenderer *cell = NULL; - GtkCellAreaContext *context; - - context = g_ptr_array_index (icon_view->priv->row_contexts, item->row); - _gtk_icon_view_set_cell_data (icon_view, item); - - if (x >= item_area->x && x <= item_area->x + item_area->width && - y >= item_area->y && y <= item_area->y + item_area->height) - cell = gtk_cell_area_get_cell_at_position (icon_view->priv->cell_area, context, - GTK_WIDGET (icon_view), - item_area, - x, y, NULL); - - if (cell_at_pos) - *cell_at_pos = cell; - - if (only_in_cell) - return cell != NULL ? item : NULL; - else - return item; - } - return item; - } - } - return NULL; -} - -void -_gtk_icon_view_select_item (GtkIconView *icon_view, - GtkIconViewItem *item) -{ - g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); - g_return_if_fail (item != NULL); - - if (item->selected) - return; - - if (icon_view->priv->selection_mode == GTK_SELECTION_NONE) - return; - else if (icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE) - gtk_icon_view_unselect_all_internal (icon_view); - - item->selected = TRUE; - - g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0); - - gtk_icon_view_queue_draw_item (icon_view, item); -} - - -void -_gtk_icon_view_unselect_item (GtkIconView *icon_view, - GtkIconViewItem *item) -{ - g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); - g_return_if_fail (item != NULL); - - if (!item->selected) - return; - - if (icon_view->priv->selection_mode == GTK_SELECTION_NONE || - icon_view->priv->selection_mode == GTK_SELECTION_BROWSE) - return; - - item->selected = FALSE; - - g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0); - - gtk_icon_view_queue_draw_item (icon_view, item); -} - -static void -verify_items (GtkIconView *icon_view) -{ - GList *items; - int i = 0; - - for (items = icon_view->priv->items; items; items = items->next) - { - GtkIconViewItem *item = items->data; - - if (item->index != i) - g_error ("List item does not match its index: " - "item index %d and list index %d\n", item->index, i); - - i++; - } -} - -static void -gtk_icon_view_row_changed (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data) -{ - GtkIconView *icon_view = GTK_ICON_VIEW (data); - - /* ignore changes in branches */ - if (gtk_tree_path_get_depth (path) > 1) - return; - - /* An icon view subclass might add it's own model and populate - * things at init() time instead of waiting for the constructor() - * to be called - */ - if (icon_view->priv->cell_area) - gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); - - /* Here we can use a "grow-only" strategy for optimization - * and only invalidate a single item and queue a relayout - * instead of invalidating the whole thing. - * - * For now GtkIconView still can't deal with huge models - * so just invalidate the whole thing when the model - * changes. - */ - gtk_icon_view_invalidate_sizes (icon_view); - - verify_items (icon_view); -} - -static void -gtk_icon_view_row_inserted (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data) -{ - GtkIconView *icon_view = GTK_ICON_VIEW (data); - int index; - GtkIconViewItem *item; - GList *list; - - /* ignore changes in branches */ - if (gtk_tree_path_get_depth (path) > 1) - return; - - gtk_tree_model_ref_node (model, iter); - - index = gtk_tree_path_get_indices(path)[0]; - - item = gtk_icon_view_item_new (); - - item->index = index; - - /* FIXME: We can be more efficient here, - we can store a tail pointer and use that when - appending (which is a rather common operation) - */ - icon_view->priv->items = g_list_insert (icon_view->priv->items, - item, index); - - list = g_list_nth (icon_view->priv->items, index + 1); - for (; list; list = list->next) - { - item = list->data; - - item->index++; - } - - verify_items (icon_view); - - gtk_widget_queue_resize (GTK_WIDGET (icon_view)); -} - -static void -gtk_icon_view_row_deleted (GtkTreeModel *model, - GtkTreePath *path, - gpointer data) -{ - GtkIconView *icon_view = GTK_ICON_VIEW (data); - int index; - GtkIconViewItem *item; - GList *list, *next; - gboolean emit = FALSE; - GtkTreeIter iter; - - /* ignore changes in branches */ - if (gtk_tree_path_get_depth (path) > 1) - return; - - if (gtk_tree_model_get_iter (model, &iter, path)) - gtk_tree_model_unref_node (model, &iter); - - index = gtk_tree_path_get_indices(path)[0]; - - list = g_list_nth (icon_view->priv->items, index); - item = list->data; - - if (icon_view->priv->cell_area) - gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); - - if (item == icon_view->priv->anchor_item) - icon_view->priv->anchor_item = NULL; - - if (item == icon_view->priv->cursor_item) - icon_view->priv->cursor_item = NULL; - - if (item == icon_view->priv->last_prelight) - icon_view->priv->last_prelight = NULL; - - if (item->selected) - emit = TRUE; - - gtk_icon_view_item_free (item); - - for (next = list->next; next; next = next->next) - { - item = next->data; - - item->index--; - } - - icon_view->priv->items = g_list_delete_link (icon_view->priv->items, list); - - verify_items (icon_view); - - gtk_widget_queue_resize (GTK_WIDGET (icon_view)); - - if (emit) - g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0); -} - -static void -gtk_icon_view_rows_reordered (GtkTreeModel *model, - GtkTreePath *parent, - GtkTreeIter *iter, - int *new_order, - gpointer data) -{ - GtkIconView *icon_view = GTK_ICON_VIEW (data); - int i; - int length; - GList *items = NULL, *list; - GtkIconViewItem **item_array; - int *order; - - /* ignore changes in branches */ - if (iter != NULL) - return; - - if (icon_view->priv->cell_area) - gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); - - length = gtk_tree_model_iter_n_children (model, NULL); - - order = g_new (int, length); - for (i = 0; i < length; i++) - order [new_order[i]] = i; - - item_array = g_new (GtkIconViewItem *, length); - for (i = 0, list = icon_view->priv->items; list != NULL; list = list->next, i++) - item_array[order[i]] = list->data; - g_free (order); - - for (i = length - 1; i >= 0; i--) - { - item_array[i]->index = i; - items = g_list_prepend (items, item_array[i]); - } - - g_free (item_array); - g_list_free (icon_view->priv->items); - icon_view->priv->items = items; - - gtk_widget_queue_resize (GTK_WIDGET (icon_view)); - - verify_items (icon_view); -} - -static void -gtk_icon_view_build_items (GtkIconView *icon_view) -{ - GtkTreeIter iter; - int i; - GList *items = NULL; - - if (!gtk_tree_model_get_iter_first (icon_view->priv->model, - &iter)) - return; - - i = 0; - - do - { - GtkIconViewItem *item = gtk_icon_view_item_new (); - - item->index = i; - - i++; - - items = g_list_prepend (items, item); - - } while (gtk_tree_model_iter_next (icon_view->priv->model, &iter)); - - icon_view->priv->items = g_list_reverse (items); -} - -static void -gtk_icon_view_add_move_binding (GtkWidgetClass *widget_class, - guint keyval, - guint modmask, - GtkMovementStep step, - int count) -{ - - gtk_widget_class_add_binding_signal (widget_class, - keyval, modmask, - I_("move-cursor"), - "(iibb)", step, count, FALSE, FALSE); - - gtk_widget_class_add_binding_signal (widget_class, - keyval, GDK_SHIFT_MASK, - "move-cursor", - "(iibb)", step, count, TRUE, FALSE); - - if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK) - return; - - gtk_widget_class_add_binding_signal (widget_class, - keyval, GDK_CONTROL_MASK | GDK_SHIFT_MASK, - "move-cursor", - "(iibb)", step, count, TRUE, TRUE); - - gtk_widget_class_add_binding_signal (widget_class, - keyval, GDK_CONTROL_MASK, - "move-cursor", - "(iibb)", step, count, FALSE, TRUE); -} - -static gboolean -gtk_icon_view_real_move_cursor (GtkIconView *icon_view, - GtkMovementStep step, - int count, - gboolean extend, - gboolean modify) -{ - g_return_val_if_fail (GTK_ICON_VIEW (icon_view), FALSE); - g_return_val_if_fail (step == GTK_MOVEMENT_LOGICAL_POSITIONS || - step == GTK_MOVEMENT_VISUAL_POSITIONS || - step == GTK_MOVEMENT_DISPLAY_LINES || - step == GTK_MOVEMENT_PAGES || - step == GTK_MOVEMENT_BUFFER_ENDS, FALSE); - - if (!gtk_widget_has_focus (GTK_WIDGET (icon_view))) - return FALSE; - - gtk_cell_area_stop_editing (icon_view->priv->cell_area, FALSE); - gtk_widget_grab_focus (GTK_WIDGET (icon_view)); - - icon_view->priv->extend_selection_pressed = extend; - icon_view->priv->modify_selection_pressed = modify; - - switch (step) - { - case GTK_MOVEMENT_LOGICAL_POSITIONS: - case GTK_MOVEMENT_VISUAL_POSITIONS: - gtk_icon_view_move_cursor_left_right (icon_view, count); - break; - case GTK_MOVEMENT_DISPLAY_LINES: - gtk_icon_view_move_cursor_up_down (icon_view, count); - break; - case GTK_MOVEMENT_PAGES: - gtk_icon_view_move_cursor_page_up_down (icon_view, count); - break; - case GTK_MOVEMENT_BUFFER_ENDS: - gtk_icon_view_move_cursor_start_end (icon_view, count); - break; - case GTK_MOVEMENT_WORDS: - case GTK_MOVEMENT_DISPLAY_LINE_ENDS: - case GTK_MOVEMENT_PARAGRAPHS: - case GTK_MOVEMENT_PARAGRAPH_ENDS: - case GTK_MOVEMENT_HORIZONTAL_PAGES: - default: - g_assert_not_reached (); - break; - } - - icon_view->priv->modify_selection_pressed = FALSE; - icon_view->priv->extend_selection_pressed = FALSE; - - icon_view->priv->draw_focus = TRUE; - - return TRUE; -} - -static GtkIconViewItem * -find_item (GtkIconView *icon_view, - GtkIconViewItem *current, - int row_ofs, - int col_ofs) -{ - int row, col; - GList *items; - GtkIconViewItem *item; - - /* FIXME: this could be more efficient - */ - row = current->row + row_ofs; - col = current->col + col_ofs; - - for (items = icon_view->priv->items; items; items = items->next) - { - item = items->data; - if (item->row == row && item->col == col) - return item; - } - - return NULL; -} - -static GtkIconViewItem * -find_item_page_up_down (GtkIconView *icon_view, - GtkIconViewItem *current, - int count) -{ - GList *item, *next; - int y, col; - - col = current->col; - y = current->cell_area.y + count * gtk_adjustment_get_page_size (icon_view->priv->vadjustment); - - item = g_list_find (icon_view->priv->items, current); - if (count > 0) - { - while (item) - { - for (next = item->next; next; next = next->next) - { - if (((GtkIconViewItem *)next->data)->col == col) - break; - } - if (!next || ((GtkIconViewItem *)next->data)->cell_area.y > y) - break; - - item = next; - } - } - else - { - while (item) - { - for (next = item->prev; next; next = next->prev) - { - if (((GtkIconViewItem *)next->data)->col == col) - break; - } - if (!next || ((GtkIconViewItem *)next->data)->cell_area.y < y) - break; - - item = next; - } - } - - if (item) - return item->data; - - return NULL; -} - -static gboolean -gtk_icon_view_select_all_between (GtkIconView *icon_view, - GtkIconViewItem *anchor, - GtkIconViewItem *cursor) -{ - GList *items; - GtkIconViewItem *item; - int row1, row2, col1, col2; - gboolean dirty = FALSE; - - if (anchor->row < cursor->row) - { - row1 = anchor->row; - row2 = cursor->row; - } - else - { - row1 = cursor->row; - row2 = anchor->row; - } - - if (anchor->col < cursor->col) - { - col1 = anchor->col; - col2 = cursor->col; - } - else - { - col1 = cursor->col; - col2 = anchor->col; - } - - for (items = icon_view->priv->items; items; items = items->next) - { - item = items->data; - - if (row1 <= item->row && item->row <= row2 && - col1 <= item->col && item->col <= col2) - { - if (!item->selected) - { - dirty = TRUE; - item->selected = TRUE; - } - gtk_icon_view_queue_draw_item (icon_view, item); - } - } - - return dirty; -} - -static void -gtk_icon_view_move_cursor_up_down (GtkIconView *icon_view, - int count) -{ - GtkIconViewItem *item; - GtkCellRenderer *cell = NULL; - gboolean dirty = FALSE; - int step; - GtkDirectionType direction; - - if (!gtk_widget_has_focus (GTK_WIDGET (icon_view))) - return; - - direction = count < 0 ? GTK_DIR_UP : GTK_DIR_DOWN; - - if (!icon_view->priv->cursor_item) - { - GList *list; - - if (count > 0) - list = icon_view->priv->items; - else - list = g_list_last (icon_view->priv->items); - - if (list) - { - item = list->data; - - /* Give focus to the first cell initially */ - _gtk_icon_view_set_cell_data (icon_view, item); - gtk_cell_area_focus (icon_view->priv->cell_area, direction); - } - else - { - item = NULL; - } - } - else - { - item = icon_view->priv->cursor_item; - step = count > 0 ? 1 : -1; - - /* Save the current focus cell in case we hit the edge */ - cell = gtk_cell_area_get_focus_cell (icon_view->priv->cell_area); - - while (item) - { - _gtk_icon_view_set_cell_data (icon_view, item); - - if (gtk_cell_area_focus (icon_view->priv->cell_area, direction)) - break; - - item = find_item (icon_view, item, step, 0); - } - } - - if (!item) - { - if (!gtk_widget_keynav_failed (GTK_WIDGET (icon_view), direction)) - { - GtkWidget *toplevel = GTK_WIDGET (gtk_widget_get_root (GTK_WIDGET (icon_view))); - if (toplevel) - gtk_widget_child_focus (toplevel, - direction == GTK_DIR_UP ? - GTK_DIR_TAB_BACKWARD : - GTK_DIR_TAB_FORWARD); - - } - - gtk_cell_area_set_focus_cell (icon_view->priv->cell_area, cell); - return; - } - - if (icon_view->priv->modify_selection_pressed || - !icon_view->priv->extend_selection_pressed || - !icon_view->priv->anchor_item || - icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE) - icon_view->priv->anchor_item = item; - - cell = gtk_cell_area_get_focus_cell (icon_view->priv->cell_area); - _gtk_icon_view_set_cursor_item (icon_view, item, cell); - - if (!icon_view->priv->modify_selection_pressed && - icon_view->priv->selection_mode != GTK_SELECTION_NONE) - { - dirty = gtk_icon_view_unselect_all_internal (icon_view); - dirty = gtk_icon_view_select_all_between (icon_view, - icon_view->priv->anchor_item, - item) || dirty; - } - - gtk_icon_view_scroll_to_item (icon_view, item); - - if (dirty) - g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0); -} - -static void -gtk_icon_view_move_cursor_page_up_down (GtkIconView *icon_view, - int count) -{ - GtkIconViewItem *item; - gboolean dirty = FALSE; - - if (!gtk_widget_has_focus (GTK_WIDGET (icon_view))) - return; - - if (!icon_view->priv->cursor_item) - { - GList *list; - - if (count > 0) - list = icon_view->priv->items; - else - list = g_list_last (icon_view->priv->items); - - item = list ? list->data : NULL; - } - else - item = find_item_page_up_down (icon_view, - icon_view->priv->cursor_item, - count); - - if (item == icon_view->priv->cursor_item) - gtk_widget_error_bell (GTK_WIDGET (icon_view)); - - if (!item) - return; - - if (icon_view->priv->modify_selection_pressed || - !icon_view->priv->extend_selection_pressed || - !icon_view->priv->anchor_item || - icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE) - icon_view->priv->anchor_item = item; - - _gtk_icon_view_set_cursor_item (icon_view, item, NULL); - - if (!icon_view->priv->modify_selection_pressed && - icon_view->priv->selection_mode != GTK_SELECTION_NONE) - { - dirty = gtk_icon_view_unselect_all_internal (icon_view); - dirty = gtk_icon_view_select_all_between (icon_view, - icon_view->priv->anchor_item, - item) || dirty; - } - - gtk_icon_view_scroll_to_item (icon_view, item); - - if (dirty) - g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0); -} - -static void -gtk_icon_view_move_cursor_left_right (GtkIconView *icon_view, - int count) -{ - GtkIconViewItem *item; - GtkCellRenderer *cell = NULL; - gboolean dirty = FALSE; - int step; - GtkDirectionType direction; - - if (!gtk_widget_has_focus (GTK_WIDGET (icon_view))) - return; - - direction = count < 0 ? GTK_DIR_LEFT : GTK_DIR_RIGHT; - - if (!icon_view->priv->cursor_item) - { - GList *list; - - if (count > 0) - list = icon_view->priv->items; - else - list = g_list_last (icon_view->priv->items); - - if (list) - { - item = list->data; - - /* Give focus to the first cell initially */ - _gtk_icon_view_set_cell_data (icon_view, item); - gtk_cell_area_focus (icon_view->priv->cell_area, direction); - } - else - { - item = NULL; - } - } - else - { - item = icon_view->priv->cursor_item; - step = count > 0 ? 1 : -1; - - /* Save the current focus cell in case we hit the edge */ - cell = gtk_cell_area_get_focus_cell (icon_view->priv->cell_area); - - while (item) - { - _gtk_icon_view_set_cell_data (icon_view, item); - - if (gtk_cell_area_focus (icon_view->priv->cell_area, direction)) - break; - - item = find_item (icon_view, item, 0, step); - } - } - - if (!item) - { - if (!gtk_widget_keynav_failed (GTK_WIDGET (icon_view), direction)) - { - GtkWidget *toplevel = GTK_WIDGET (gtk_widget_get_root (GTK_WIDGET (icon_view))); - if (toplevel) - gtk_widget_child_focus (toplevel, - direction == GTK_DIR_LEFT ? - GTK_DIR_TAB_BACKWARD : - GTK_DIR_TAB_FORWARD); - - } - - gtk_cell_area_set_focus_cell (icon_view->priv->cell_area, cell); - return; - } - - if (icon_view->priv->modify_selection_pressed || - !icon_view->priv->extend_selection_pressed || - !icon_view->priv->anchor_item || - icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE) - icon_view->priv->anchor_item = item; - - cell = gtk_cell_area_get_focus_cell (icon_view->priv->cell_area); - _gtk_icon_view_set_cursor_item (icon_view, item, cell); - - if (!icon_view->priv->modify_selection_pressed && - icon_view->priv->selection_mode != GTK_SELECTION_NONE) - { - dirty = gtk_icon_view_unselect_all_internal (icon_view); - dirty = gtk_icon_view_select_all_between (icon_view, - icon_view->priv->anchor_item, - item) || dirty; - } - - gtk_icon_view_scroll_to_item (icon_view, item); - - if (dirty) - g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0); -} - -static void -gtk_icon_view_move_cursor_start_end (GtkIconView *icon_view, - int count) -{ - GtkIconViewItem *item; - GList *list; - gboolean dirty = FALSE; - - if (!gtk_widget_has_focus (GTK_WIDGET (icon_view))) - return; - - if (count < 0) - list = icon_view->priv->items; - else - list = g_list_last (icon_view->priv->items); - - item = list ? list->data : NULL; - - if (item == icon_view->priv->cursor_item) - gtk_widget_error_bell (GTK_WIDGET (icon_view)); - - if (!item) - return; - - if (icon_view->priv->modify_selection_pressed || - !icon_view->priv->extend_selection_pressed || - !icon_view->priv->anchor_item || - icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE) - icon_view->priv->anchor_item = item; - - _gtk_icon_view_set_cursor_item (icon_view, item, NULL); - - if (!icon_view->priv->modify_selection_pressed && - icon_view->priv->selection_mode != GTK_SELECTION_NONE) - { - dirty = gtk_icon_view_unselect_all_internal (icon_view); - dirty = gtk_icon_view_select_all_between (icon_view, - icon_view->priv->anchor_item, - item) || dirty; - } - - gtk_icon_view_scroll_to_item (icon_view, item); - - if (dirty) - g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0); -} - -/** - * gtk_icon_view_scroll_to_path: - * @icon_view: A `GtkIconView` - * @path: The path of the item to move to. - * @use_align: whether to use alignment arguments, or %FALSE. - * @row_align: The vertical alignment of the item specified by @path. - * @col_align: The horizontal alignment of the item specified by @path. - * - * Moves the alignments of @icon_view to the position specified by @path. - * @row_align determines where the row is placed, and @col_align determines - * where @column is placed. Both are expected to be between 0.0 and 1.0. - * 0.0 means left/top alignment, 1.0 means right/bottom alignment, 0.5 means - * center. - * - * If @use_align is %FALSE, then the alignment arguments are ignored, and the - * tree does the minimum amount of work to scroll the item onto the screen. - * This means that the item will be scrolled to the edge closest to its current - * position. If the item is currently visible on the screen, nothing is done. - * - * This function only works if the model is set, and @path is a valid row on - * the model. If the model changes before the @icon_view is realized, the - * centered path will be modified to reflect this change. - **/ -void -gtk_icon_view_scroll_to_path (GtkIconView *icon_view, - GtkTreePath *path, - gboolean use_align, - float row_align, - float col_align) -{ - GtkIconViewItem *item = NULL; - GtkWidget *widget; - - g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); - g_return_if_fail (path != NULL); - g_return_if_fail (row_align >= 0.0 && row_align <= 1.0); - g_return_if_fail (col_align >= 0.0 && col_align <= 1.0); - - widget = GTK_WIDGET (icon_view); - - if (gtk_tree_path_get_depth (path) > 0) - item = g_list_nth_data (icon_view->priv->items, - gtk_tree_path_get_indices(path)[0]); - - if (!item || item->cell_area.width < 0 || - !gtk_widget_get_realized (widget)) - { - if (icon_view->priv->scroll_to_path) - gtk_tree_row_reference_free (icon_view->priv->scroll_to_path); - - icon_view->priv->scroll_to_path = NULL; - - if (path) - icon_view->priv->scroll_to_path = gtk_tree_row_reference_new_proxy (G_OBJECT (icon_view), icon_view->priv->model, path); - - icon_view->priv->scroll_to_use_align = use_align; - icon_view->priv->scroll_to_row_align = row_align; - icon_view->priv->scroll_to_col_align = col_align; - - return; - } - - if (use_align) - { - int width, height; - int x, y; - float offset; - GdkRectangle item_area = - { - item->cell_area.x - icon_view->priv->item_padding, - item->cell_area.y - icon_view->priv->item_padding, - item->cell_area.width + icon_view->priv->item_padding * 2, - item->cell_area.height + icon_view->priv->item_padding * 2 - }; - - x =0; - y =0; - - width = gtk_widget_get_width (widget); - height = gtk_widget_get_height (widget); - - offset = y + item_area.y - row_align * (height - item_area.height); - - gtk_adjustment_set_value (icon_view->priv->vadjustment, - gtk_adjustment_get_value (icon_view->priv->vadjustment) + offset); - - offset = x + item_area.x - col_align * (width - item_area.width); - - gtk_adjustment_set_value (icon_view->priv->hadjustment, - gtk_adjustment_get_value (icon_view->priv->hadjustment) + offset); - } - else - gtk_icon_view_scroll_to_item (icon_view, item); -} - - -static void -gtk_icon_view_scroll_to_item (GtkIconView *icon_view, - GtkIconViewItem *item) -{ - GtkIconViewPrivate *priv = icon_view->priv; - GtkWidget *widget = GTK_WIDGET (icon_view); - GtkAdjustment *hadj, *vadj; - int widget_width, widget_height; - int x, y; - GdkRectangle item_area; - - item_area.x = item->cell_area.x - priv->item_padding; - item_area.y = item->cell_area.y - priv->item_padding; - item_area.width = item->cell_area.width + priv->item_padding * 2; - item_area.height = item->cell_area.height + priv->item_padding * 2; - - widget_width = gtk_widget_get_width (widget); - widget_height = gtk_widget_get_height (widget); - - hadj = icon_view->priv->hadjustment; - vadj = icon_view->priv->vadjustment; - - x = - gtk_adjustment_get_value (hadj); - y = - gtk_adjustment_get_value (vadj); - - if (y + item_area.y < 0) - gtk_adjustment_animate_to_value (vadj, - gtk_adjustment_get_value (vadj) - + y + item_area.y); - else if (y + item_area.y + item_area.height > widget_height) - gtk_adjustment_animate_to_value (vadj, - gtk_adjustment_get_value (vadj) - + y + item_area.y + item_area.height - widget_height); - - if (x + item_area.x < 0) - gtk_adjustment_animate_to_value (hadj, - gtk_adjustment_get_value (hadj) - + x + item_area.x); - else if (x + item_area.x + item_area.width > widget_width) - gtk_adjustment_animate_to_value (hadj, - gtk_adjustment_get_value (hadj) - + x + item_area.x + item_area.width - widget_width); -} - -/* GtkCellLayout implementation */ - -static void -gtk_icon_view_ensure_cell_area (GtkIconView *icon_view, - GtkCellArea *cell_area) -{ - GtkIconViewPrivate *priv = icon_view->priv; - - if (priv->cell_area) - return; - - if (cell_area) - priv->cell_area = cell_area; - else - priv->cell_area = gtk_cell_area_box_new (); - - g_object_ref_sink (priv->cell_area); - - if (GTK_IS_ORIENTABLE (priv->cell_area)) - gtk_orientable_set_orientation (GTK_ORIENTABLE (priv->cell_area), priv->item_orientation); - - priv->cell_area_context = gtk_cell_area_create_context (priv->cell_area); - - priv->add_editable_id = - g_signal_connect (priv->cell_area, "add-editable", - G_CALLBACK (gtk_icon_view_add_editable), icon_view); - priv->remove_editable_id = - g_signal_connect (priv->cell_area, "remove-editable", - G_CALLBACK (gtk_icon_view_remove_editable), icon_view); - - update_text_cell (icon_view); - update_pixbuf_cell (icon_view); -} - -static GtkCellArea * -gtk_icon_view_cell_layout_get_area (GtkCellLayout *cell_layout) -{ - GtkIconView *icon_view = GTK_ICON_VIEW (cell_layout); - GtkIconViewPrivate *priv = icon_view->priv; - - if (G_UNLIKELY (!priv->cell_area)) - gtk_icon_view_ensure_cell_area (icon_view, NULL); - - return icon_view->priv->cell_area; -} - -void -_gtk_icon_view_set_cell_data (GtkIconView *icon_view, - GtkIconViewItem *item) -{ - GtkTreeIter iter; - GtkTreePath *path; - - path = gtk_tree_path_new_from_indices (item->index, -1); - if (!gtk_tree_model_get_iter (icon_view->priv->model, &iter, path)) - return; - gtk_tree_path_free (path); - - gtk_cell_area_apply_attributes (icon_view->priv->cell_area, - icon_view->priv->model, - &iter, FALSE, FALSE); -} - - - -/* Public API */ - - -/** - * gtk_icon_view_new: - * - * Creates a new `GtkIconView` widget - * - * Returns: A newly created `GtkIconView` widget - **/ -GtkWidget * -gtk_icon_view_new (void) -{ - return g_object_new (GTK_TYPE_ICON_VIEW, NULL); -} - -/** - * gtk_icon_view_new_with_area: - * @area: the `GtkCellArea` to use to layout cells - * - * Creates a new `GtkIconView` widget using the - * specified @area to layout cells inside the icons. - * - * Returns: A newly created `GtkIconView` widget - **/ -GtkWidget * -gtk_icon_view_new_with_area (GtkCellArea *area) -{ - return g_object_new (GTK_TYPE_ICON_VIEW, "cell-area", area, NULL); -} - -/** - * gtk_icon_view_new_with_model: - * @model: The model. - * - * Creates a new `GtkIconView` widget with the model @model. - * - * Returns: A newly created `GtkIconView` widget. - **/ -GtkWidget * -gtk_icon_view_new_with_model (GtkTreeModel *model) -{ - return g_object_new (GTK_TYPE_ICON_VIEW, "model", model, NULL); -} - -/** - * gtk_icon_view_get_path_at_pos: - * @icon_view: A `GtkIconView`. - * @x: The x position to be identified - * @y: The y position to be identified - * - * Gets the path for the icon at the given position. - * - * Returns: (nullable) (transfer full): The `GtkTreePath` corresponding - * to the icon or %NULL if no icon exists at that position. - **/ -GtkTreePath * -gtk_icon_view_get_path_at_pos (GtkIconView *icon_view, - int x, - int y) -{ - GtkIconViewItem *item; - GtkTreePath *path; - - g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), NULL); - - item = _gtk_icon_view_get_item_at_coords (icon_view, x, y, TRUE, NULL); - - if (!item) - return NULL; - - path = gtk_tree_path_new_from_indices (item->index, -1); - - return path; -} - -/** - * gtk_icon_view_get_item_at_pos: - * @icon_view: A `GtkIconView`. - * @x: The x position to be identified - * @y: The y position to be identified - * @path: (out) (optional): Return location for the path - * @cell: (out) (optional) (transfer none): Return location for the renderer - * responsible for the cell at (@x, @y) - * - * Gets the path and cell for the icon at the given position. - * - * Returns: %TRUE if an item exists at the specified position - **/ -gboolean -gtk_icon_view_get_item_at_pos (GtkIconView *icon_view, - int x, - int y, - GtkTreePath **path, - GtkCellRenderer **cell) -{ - GtkIconViewItem *item; - GtkCellRenderer *renderer = NULL; - - g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), FALSE); - - item = _gtk_icon_view_get_item_at_coords (icon_view, x, y, TRUE, &renderer); - - if (path != NULL) - { - if (item != NULL) - *path = gtk_tree_path_new_from_indices (item->index, -1); - else - *path = NULL; - } - - if (cell != NULL) - *cell = renderer; - - return (item != NULL); -} - -/** - * gtk_icon_view_get_cell_rect: - * @icon_view: a `GtkIconView` - * @path: a `GtkTreePath` - * @cell: (nullable): a `GtkCellRenderer` - * @rect: (out): rectangle to fill with cell rect - * - * Fills the bounding rectangle in widget coordinates for the cell specified by - * @path and @cell. If @cell is %NULL the main cell area is used. - * - * This function is only valid if @icon_view is realized. - * - * Returns: %FALSE if there is no such item, %TRUE otherwise - */ -gboolean -gtk_icon_view_get_cell_rect (GtkIconView *icon_view, - GtkTreePath *path, - GtkCellRenderer *cell, - GdkRectangle *rect) -{ - GtkIconViewItem *item = NULL; - - g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), FALSE); - g_return_val_if_fail (cell == NULL || GTK_IS_CELL_RENDERER (cell), FALSE); - - if (gtk_tree_path_get_depth (path) > 0) - item = g_list_nth_data (icon_view->priv->items, - gtk_tree_path_get_indices(path)[0]); - - if (!item) - return FALSE; - - if (cell) - { - GtkCellAreaContext *context; - - context = g_ptr_array_index (icon_view->priv->row_contexts, item->row); - _gtk_icon_view_set_cell_data (icon_view, item); - gtk_cell_area_get_cell_allocation (icon_view->priv->cell_area, context, - GTK_WIDGET (icon_view), - cell, &item->cell_area, rect); - } - else - { - rect->x = item->cell_area.x - icon_view->priv->item_padding; - rect->y = item->cell_area.y - icon_view->priv->item_padding; - rect->width = item->cell_area.width + icon_view->priv->item_padding * 2; - rect->height = item->cell_area.height + icon_view->priv->item_padding * 2; - } - - return TRUE; -} - -/** - * gtk_icon_view_set_tooltip_item: - * @icon_view: a `GtkIconView` - * @tooltip: a `GtkTooltip` - * @path: a `GtkTreePath` - * - * Sets the tip area of @tooltip to be the area covered by the item at @path. - * See also gtk_icon_view_set_tooltip_column() for a simpler alternative. - * See also gtk_tooltip_set_tip_area(). - */ -void -gtk_icon_view_set_tooltip_item (GtkIconView *icon_view, - GtkTooltip *tooltip, - GtkTreePath *path) -{ - g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); - g_return_if_fail (GTK_IS_TOOLTIP (tooltip)); - - gtk_icon_view_set_tooltip_cell (icon_view, tooltip, path, NULL); -} - -/** - * gtk_icon_view_set_tooltip_cell: - * @icon_view: a `GtkIconView` - * @tooltip: a `GtkTooltip` - * @path: a `GtkTreePath` - * @cell: (nullable): a `GtkCellRenderer` - * - * Sets the tip area of @tooltip to the area which @cell occupies in - * the item pointed to by @path. See also gtk_tooltip_set_tip_area(). - * - * See also gtk_icon_view_set_tooltip_column() for a simpler alternative. - */ -void -gtk_icon_view_set_tooltip_cell (GtkIconView *icon_view, - GtkTooltip *tooltip, - GtkTreePath *path, - GtkCellRenderer *cell) -{ - GdkRectangle rect; - - g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); - g_return_if_fail (GTK_IS_TOOLTIP (tooltip)); - g_return_if_fail (cell == NULL || GTK_IS_CELL_RENDERER (cell)); - - if (!gtk_icon_view_get_cell_rect (icon_view, path, cell, &rect)) - return; - - gtk_tooltip_set_tip_area (tooltip, &rect); -} - - -/** - * gtk_icon_view_get_tooltip_context: - * @icon_view: an `GtkIconView` - * @x: the x coordinate (relative to widget coordinates) - * @y: the y coordinate (relative to widget coordinates) - * @keyboard_tip: whether this is a keyboard tooltip or not - * @model: (out) (optional) (transfer none): a pointer to receive a `GtkTreeModel` - * @path: (out) (optional): a pointer to receive a `GtkTreePath` - * @iter: (out) (optional): a pointer to receive a `GtkTreeIter` - * - * This function is supposed to be used in a `GtkWidget::query-tooltip` - * signal handler for `GtkIconView`. The @x, @y and @keyboard_tip values - * which are received in the signal handler, should be passed to this - * function without modification. - * - * The return value indicates whether there is an icon view item at the given - * coordinates (%TRUE) or not (%FALSE) for mouse tooltips. For keyboard - * tooltips the item returned will be the cursor item. When %TRUE, then any of - * @model, @path and @iter which have been provided will be set to point to - * that row and the corresponding model. - * - * Returns: whether or not the given tooltip context points to an item - */ -gboolean -gtk_icon_view_get_tooltip_context (GtkIconView *icon_view, - int x, - int y, - gboolean keyboard_tip, - GtkTreeModel **model, - GtkTreePath **path, - GtkTreeIter *iter) -{ - GtkTreePath *tmppath = NULL; - - g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), FALSE); - - if (keyboard_tip) - { - gtk_icon_view_get_cursor (icon_view, &tmppath, NULL); - - if (!tmppath) - return FALSE; - } - else - { - if (!gtk_icon_view_get_item_at_pos (icon_view, x, y, &tmppath, NULL)) - return FALSE; - } - - if (model) - *model = gtk_icon_view_get_model (icon_view); - - if (iter) - gtk_tree_model_get_iter (gtk_icon_view_get_model (icon_view), - iter, tmppath); - - if (path) - *path = tmppath; - else - gtk_tree_path_free (tmppath); - - return TRUE; -} - -static gboolean -gtk_icon_view_set_tooltip_query_cb (GtkWidget *widget, - int x, - int y, - gboolean keyboard_tip, - GtkTooltip *tooltip, - gpointer data) -{ - char *str; - GtkTreeIter iter; - GtkTreePath *path; - GtkTreeModel *model; - GtkIconView *icon_view = GTK_ICON_VIEW (widget); - - if (!gtk_icon_view_get_tooltip_context (GTK_ICON_VIEW (widget), - x, y, - keyboard_tip, - &model, &path, &iter)) - return FALSE; - - gtk_tree_model_get (model, &iter, icon_view->priv->tooltip_column, &str, -1); - - if (!str) - { - gtk_tree_path_free (path); - return FALSE; - } - - gtk_tooltip_set_markup (tooltip, str); - gtk_icon_view_set_tooltip_item (icon_view, tooltip, path); - - gtk_tree_path_free (path); - g_free (str); - - return TRUE; -} - - -/** - * gtk_icon_view_set_tooltip_column: - * @icon_view: a `GtkIconView` - * @column: an integer, which is a valid column number for @icon_view’s model - * - * If you only plan to have simple (text-only) tooltips on full items, you - * can use this function to have `GtkIconView` handle these automatically - * for you. @column should be set to the column in @icon_view’s model - * containing the tooltip texts, or -1 to disable this feature. - * - * When enabled, `GtkWidget:has-tooltip` will be set to %TRUE and - * @icon_view will connect a `GtkWidget::query-tooltip` signal handler. - * - * Note that the signal handler sets the text with gtk_tooltip_set_markup(), - * so &, <, etc have to be escaped in the text. - */ -void -gtk_icon_view_set_tooltip_column (GtkIconView *icon_view, - int column) -{ - g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); - - if (column == icon_view->priv->tooltip_column) - return; - - if (column == -1) - { - g_signal_handlers_disconnect_by_func (icon_view, - gtk_icon_view_set_tooltip_query_cb, - NULL); - gtk_widget_set_has_tooltip (GTK_WIDGET (icon_view), FALSE); - } - else - { - if (icon_view->priv->tooltip_column == -1) - { - g_signal_connect (icon_view, "query-tooltip", - G_CALLBACK (gtk_icon_view_set_tooltip_query_cb), NULL); - gtk_widget_set_has_tooltip (GTK_WIDGET (icon_view), TRUE); - } - } - - icon_view->priv->tooltip_column = column; - g_object_notify (G_OBJECT (icon_view), "tooltip-column"); -} - -/** - * gtk_icon_view_get_tooltip_column: - * @icon_view: a `GtkIconView` - * - * Returns the column of @icon_view’s model which is being used for - * displaying tooltips on @icon_view’s rows. - * - * Returns: the index of the tooltip column that is currently being - * used, or -1 if this is disabled. - */ -int -gtk_icon_view_get_tooltip_column (GtkIconView *icon_view) -{ - g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), 0); - - return icon_view->priv->tooltip_column; -} - -/** - * gtk_icon_view_get_visible_range: - * @icon_view: A `GtkIconView` - * @start_path: (out) (optional): Return location for start of region - * @end_path: (out) (optional): Return location for end of region - * - * Sets @start_path and @end_path to be the first and last visible path. - * Note that there may be invisible paths in between. - * - * Both paths should be freed with gtk_tree_path_free() after use. - * - * Returns: %TRUE, if valid paths were placed in @start_path and @end_path - */ -gboolean -gtk_icon_view_get_visible_range (GtkIconView *icon_view, - GtkTreePath **start_path, - GtkTreePath **end_path) -{ - int start_index = -1; - int end_index = -1; - GList *icons; - - g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), FALSE); - - if (icon_view->priv->hadjustment == NULL || - icon_view->priv->vadjustment == NULL) - return FALSE; - - if (start_path == NULL && end_path == NULL) - return FALSE; - - for (icons = icon_view->priv->items; icons; icons = icons->next) - { - GtkIconViewItem *item = icons->data; - GdkRectangle *item_area = &item->cell_area; - - if ((item_area->x + item_area->width >= (int)gtk_adjustment_get_value (icon_view->priv->hadjustment)) && - (item_area->y + item_area->height >= (int)gtk_adjustment_get_value (icon_view->priv->vadjustment)) && - (item_area->x <= - (int) (gtk_adjustment_get_value (icon_view->priv->hadjustment) + - gtk_adjustment_get_page_size (icon_view->priv->hadjustment))) && - (item_area->y <= - (int) (gtk_adjustment_get_value (icon_view->priv->vadjustment) + - gtk_adjustment_get_page_size (icon_view->priv->vadjustment)))) - { - if (start_index == -1) - start_index = item->index; - end_index = item->index; - } - } - - if (start_path && start_index != -1) - *start_path = gtk_tree_path_new_from_indices (start_index, -1); - if (end_path && end_index != -1) - *end_path = gtk_tree_path_new_from_indices (end_index, -1); - - return start_index != -1; -} - -/** - * gtk_icon_view_selected_foreach: - * @icon_view: A `GtkIconView`. - * @func: (scope call): The function to call for each selected icon. - * @data: User data to pass to the function. - * - * Calls a function for each selected icon. Note that the model or - * selection cannot be modified from within this function. - **/ -void -gtk_icon_view_selected_foreach (GtkIconView *icon_view, - GtkIconViewForeachFunc func, - gpointer data) -{ - GList *list; - - for (list = icon_view->priv->items; list; list = list->next) - { - GtkIconViewItem *item = list->data; - GtkTreePath *path = gtk_tree_path_new_from_indices (item->index, -1); - - if (item->selected) - (* func) (icon_view, path, data); - - gtk_tree_path_free (path); - } -} - -/** - * gtk_icon_view_set_selection_mode: - * @icon_view: A `GtkIconView`. - * @mode: The selection mode - * - * Sets the selection mode of the @icon_view. - **/ -void -gtk_icon_view_set_selection_mode (GtkIconView *icon_view, - GtkSelectionMode mode) -{ - g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); - - if (mode == icon_view->priv->selection_mode) - return; - - if (mode == GTK_SELECTION_NONE || - icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE) - gtk_icon_view_unselect_all (icon_view); - - icon_view->priv->selection_mode = mode; - - g_object_notify (G_OBJECT (icon_view), "selection-mode"); -} - -/** - * gtk_icon_view_get_selection_mode: - * @icon_view: A `GtkIconView`. - * - * Gets the selection mode of the @icon_view. - * - * Returns: the current selection mode - **/ -GtkSelectionMode -gtk_icon_view_get_selection_mode (GtkIconView *icon_view) -{ - g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), GTK_SELECTION_SINGLE); - - return icon_view->priv->selection_mode; -} - -/** - * gtk_icon_view_set_model: - * @icon_view: A `GtkIconView`. - * @model: (nullable): The model. - * - * Sets the model for a `GtkIconView`. - * If the @icon_view already has a model set, it will remove - * it before setting the new model. If @model is %NULL, then - * it will unset the old model. - **/ -void -gtk_icon_view_set_model (GtkIconView *icon_view, - GtkTreeModel *model) -{ - gboolean dirty; - - g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); - g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model)); - - if (icon_view->priv->model == model) - return; - - if (icon_view->priv->scroll_to_path) - { - gtk_tree_row_reference_free (icon_view->priv->scroll_to_path); - icon_view->priv->scroll_to_path = NULL; - } - - /* The area can be NULL while disposing */ - if (icon_view->priv->cell_area) - gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); - - dirty = gtk_icon_view_unselect_all_internal (icon_view); - - if (model) - { - if (icon_view->priv->pixbuf_column != -1) - { - g_return_if_fail (gtk_tree_model_get_column_type (model, icon_view->priv->pixbuf_column) == GDK_TYPE_PIXBUF); - } - - if (icon_view->priv->text_column != -1) - { - g_return_if_fail (gtk_tree_model_get_column_type (model, icon_view->priv->text_column) == G_TYPE_STRING); - } - - if (icon_view->priv->markup_column != -1) - { - g_return_if_fail (gtk_tree_model_get_column_type (model, icon_view->priv->markup_column) == G_TYPE_STRING); - } - - } - - if (icon_view->priv->model) - { - g_signal_handlers_disconnect_by_func (icon_view->priv->model, - gtk_icon_view_row_changed, - icon_view); - g_signal_handlers_disconnect_by_func (icon_view->priv->model, - gtk_icon_view_row_inserted, - icon_view); - g_signal_handlers_disconnect_by_func (icon_view->priv->model, - gtk_icon_view_row_deleted, - icon_view); - g_signal_handlers_disconnect_by_func (icon_view->priv->model, - gtk_icon_view_rows_reordered, - icon_view); - - g_object_unref (icon_view->priv->model); - - g_list_free_full (icon_view->priv->items, (GDestroyNotify) gtk_icon_view_item_free); - icon_view->priv->items = NULL; - icon_view->priv->anchor_item = NULL; - icon_view->priv->cursor_item = NULL; - icon_view->priv->last_single_clicked = NULL; - icon_view->priv->last_prelight = NULL; - icon_view->priv->width = 0; - icon_view->priv->height = 0; - } - - icon_view->priv->model = model; - - if (icon_view->priv->model) - { - g_object_ref (icon_view->priv->model); - g_signal_connect (icon_view->priv->model, - "row-changed", - G_CALLBACK (gtk_icon_view_row_changed), - icon_view); - g_signal_connect (icon_view->priv->model, - "row-inserted", - G_CALLBACK (gtk_icon_view_row_inserted), - icon_view); - g_signal_connect (icon_view->priv->model, - "row-deleted", - G_CALLBACK (gtk_icon_view_row_deleted), - icon_view); - g_signal_connect (icon_view->priv->model, - "rows-reordered", - G_CALLBACK (gtk_icon_view_rows_reordered), - icon_view); - - gtk_icon_view_build_items (icon_view); - } - - g_object_notify (G_OBJECT (icon_view), "model"); - - if (dirty) - g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0); - - gtk_widget_queue_resize (GTK_WIDGET (icon_view)); -} - -/** - * gtk_icon_view_get_model: - * @icon_view: a `GtkIconView` - * - * Returns the model the `GtkIconView` is based on. Returns %NULL if the - * model is unset. - * - * Returns: (nullable) (transfer none): The currently used `GtkTreeModel` - */ -GtkTreeModel * -gtk_icon_view_get_model (GtkIconView *icon_view) -{ - g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), NULL); - - return icon_view->priv->model; -} - -static void -update_text_cell (GtkIconView *icon_view) -{ - if (!icon_view->priv->cell_area) - return; - - if (icon_view->priv->text_column == -1 && - icon_view->priv->markup_column == -1) - { - if (icon_view->priv->text_cell != NULL) - { - gtk_cell_area_remove (icon_view->priv->cell_area, - icon_view->priv->text_cell); - icon_view->priv->text_cell = NULL; - } - } - else - { - if (icon_view->priv->text_cell == NULL) - { - icon_view->priv->text_cell = gtk_cell_renderer_text_new (); - - gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (icon_view), icon_view->priv->text_cell, FALSE); - } - - if (icon_view->priv->markup_column != -1) - gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (icon_view), - icon_view->priv->text_cell, - "markup", icon_view->priv->markup_column, - NULL); - else - gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (icon_view), - icon_view->priv->text_cell, - "text", icon_view->priv->text_column, - NULL); - - if (icon_view->priv->item_orientation == GTK_ORIENTATION_VERTICAL) - g_object_set (icon_view->priv->text_cell, - "alignment", PANGO_ALIGN_CENTER, - "wrap-mode", PANGO_WRAP_WORD_CHAR, - "xalign", 0.5, - "yalign", 0.0, - NULL); - else - g_object_set (icon_view->priv->text_cell, - "alignment", PANGO_ALIGN_LEFT, - "wrap-mode", PANGO_WRAP_WORD_CHAR, - "xalign", 0.0, - "yalign", 0.5, - NULL); - } -} - -static void -update_pixbuf_cell (GtkIconView *icon_view) -{ - if (!icon_view->priv->cell_area) - return; - - if (icon_view->priv->pixbuf_column == -1) - { - if (icon_view->priv->pixbuf_cell != NULL) - { - gtk_cell_area_remove (icon_view->priv->cell_area, - icon_view->priv->pixbuf_cell); - - icon_view->priv->pixbuf_cell = NULL; - } - } - else - { - if (icon_view->priv->pixbuf_cell == NULL) - { - icon_view->priv->pixbuf_cell = gtk_cell_renderer_pixbuf_new (); - - gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (icon_view), icon_view->priv->pixbuf_cell, FALSE); - } - - gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (icon_view), - icon_view->priv->pixbuf_cell, - "pixbuf", icon_view->priv->pixbuf_column, - NULL); - - if (icon_view->priv->item_orientation == GTK_ORIENTATION_VERTICAL) - g_object_set (icon_view->priv->pixbuf_cell, - "xalign", 0.5, - "yalign", 1.0, - NULL); - else - g_object_set (icon_view->priv->pixbuf_cell, - "xalign", 0.0, - "yalign", 0.0, - NULL); - } -} - -/** - * gtk_icon_view_set_text_column: - * @icon_view: A `GtkIconView`. - * @column: A column in the currently used model, or -1 to display no text - * - * Sets the column with text for @icon_view to be @column. The text - * column must be of type `G_TYPE_STRING`. - **/ -void -gtk_icon_view_set_text_column (GtkIconView *icon_view, - int column) -{ - if (column == icon_view->priv->text_column) - return; - - if (column == -1) - icon_view->priv->text_column = -1; - else - { - if (icon_view->priv->model != NULL) - { - g_return_if_fail (gtk_tree_model_get_column_type (icon_view->priv->model, column) == G_TYPE_STRING); - } - - icon_view->priv->text_column = column; - } - - if (icon_view->priv->cell_area) - gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); - - update_text_cell (icon_view); - - gtk_icon_view_invalidate_sizes (icon_view); - - g_object_notify (G_OBJECT (icon_view), "text-column"); -} - -/** - * gtk_icon_view_get_text_column: - * @icon_view: A `GtkIconView`. - * - * Returns the column with text for @icon_view. - * - * Returns: the text column, or -1 if it’s unset. - */ -int -gtk_icon_view_get_text_column (GtkIconView *icon_view) -{ - g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), -1); - - return icon_view->priv->text_column; -} - -/** - * gtk_icon_view_set_markup_column: - * @icon_view: A `GtkIconView`. - * @column: A column in the currently used model, or -1 to display no text - * - * Sets the column with markup information for @icon_view to be - * @column. The markup column must be of type `G_TYPE_STRING`. - * If the markup column is set to something, it overrides - * the text column set by gtk_icon_view_set_text_column(). - **/ -void -gtk_icon_view_set_markup_column (GtkIconView *icon_view, - int column) -{ - if (column == icon_view->priv->markup_column) - return; - - if (column == -1) - icon_view->priv->markup_column = -1; - else - { - if (icon_view->priv->model != NULL) - { - g_return_if_fail (gtk_tree_model_get_column_type (icon_view->priv->model, column) == G_TYPE_STRING); - } - - icon_view->priv->markup_column = column; - } - - if (icon_view->priv->cell_area) - gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); - - update_text_cell (icon_view); - - gtk_icon_view_invalidate_sizes (icon_view); - - g_object_notify (G_OBJECT (icon_view), "markup-column"); -} - -/** - * gtk_icon_view_get_markup_column: - * @icon_view: A `GtkIconView`. - * - * Returns the column with markup text for @icon_view. - * - * Returns: the markup column, or -1 if it’s unset. - */ -int -gtk_icon_view_get_markup_column (GtkIconView *icon_view) -{ - g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), -1); - - return icon_view->priv->markup_column; -} - -/** - * gtk_icon_view_set_pixbuf_column: - * @icon_view: A `GtkIconView`. - * @column: A column in the currently used model, or -1 to disable - * - * Sets the column with pixbufs for @icon_view to be @column. The pixbuf - * column must be of type `GDK_TYPE_PIXBUF` - **/ -void -gtk_icon_view_set_pixbuf_column (GtkIconView *icon_view, - int column) -{ - if (column == icon_view->priv->pixbuf_column) - return; - - if (column == -1) - icon_view->priv->pixbuf_column = -1; - else - { - if (icon_view->priv->model != NULL) - { - g_return_if_fail (gtk_tree_model_get_column_type (icon_view->priv->model, column) == GDK_TYPE_PIXBUF); - } - - icon_view->priv->pixbuf_column = column; - } - - if (icon_view->priv->cell_area) - gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); - - update_pixbuf_cell (icon_view); - - gtk_icon_view_invalidate_sizes (icon_view); - - g_object_notify (G_OBJECT (icon_view), "pixbuf-column"); - -} - -/** - * gtk_icon_view_get_pixbuf_column: - * @icon_view: A `GtkIconView`. - * - * Returns the column with pixbufs for @icon_view. - * - * Returns: the pixbuf column, or -1 if it’s unset. - */ -int -gtk_icon_view_get_pixbuf_column (GtkIconView *icon_view) -{ - g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), -1); - - return icon_view->priv->pixbuf_column; -} - -/** - * gtk_icon_view_select_path: - * @icon_view: A `GtkIconView`. - * @path: The `GtkTreePath` to be selected. - * - * Selects the row at @path. - **/ -void -gtk_icon_view_select_path (GtkIconView *icon_view, - GtkTreePath *path) -{ - GtkIconViewItem *item = NULL; - - g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); - g_return_if_fail (icon_view->priv->model != NULL); - g_return_if_fail (path != NULL); - - if (gtk_tree_path_get_depth (path) > 0) - item = g_list_nth_data (icon_view->priv->items, - gtk_tree_path_get_indices(path)[0]); - - if (item) - _gtk_icon_view_select_item (icon_view, item); -} - -/** - * gtk_icon_view_unselect_path: - * @icon_view: A `GtkIconView`. - * @path: The `GtkTreePath` to be unselected. - * - * Unselects the row at @path. - **/ -void -gtk_icon_view_unselect_path (GtkIconView *icon_view, - GtkTreePath *path) -{ - GtkIconViewItem *item; - - g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); - g_return_if_fail (icon_view->priv->model != NULL); - g_return_if_fail (path != NULL); - - item = g_list_nth_data (icon_view->priv->items, - gtk_tree_path_get_indices(path)[0]); - - if (!item) - return; - - _gtk_icon_view_unselect_item (icon_view, item); -} - -/** - * gtk_icon_view_get_selected_items: - * @icon_view: A `GtkIconView`. - * - * Creates a list of paths of all selected items. Additionally, if you are - * planning on modifying the model after calling this function, you may - * want to convert the returned list into a list of `GtkTreeRowReferences`. - * To do this, you can use gtk_tree_row_reference_new(). - * - * To free the return value, use `g_list_free_full`: - * |[ - * GtkWidget *icon_view = gtk_icon_view_new (); - * // Use icon_view - * - * GList *list = gtk_icon_view_get_selected_items (GTK_ICON_VIEW (icon_view)); - * - * // use list - * - * g_list_free_full (list, (GDestroyNotify) gtk_tree_path_free); - * ]| - * - * Returns: (element-type GtkTreePath) (transfer full): A `GList` containing a `GtkTreePath` for each selected row. - **/ -GList * -gtk_icon_view_get_selected_items (GtkIconView *icon_view) -{ - GList *list; - GList *selected = NULL; - - g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), NULL); - - for (list = icon_view->priv->items; list != NULL; list = list->next) - { - GtkIconViewItem *item = list->data; - - if (item->selected) - { - GtkTreePath *path = gtk_tree_path_new_from_indices (item->index, -1); - - selected = g_list_prepend (selected, path); - } - } - - return selected; -} - -/** - * gtk_icon_view_select_all: - * @icon_view: A `GtkIconView`. - * - * Selects all the icons. @icon_view must has its selection mode set - * to %GTK_SELECTION_MULTIPLE. - **/ -void -gtk_icon_view_select_all (GtkIconView *icon_view) -{ - GList *items; - gboolean dirty = FALSE; - - g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); - - if (icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE) - return; - - for (items = icon_view->priv->items; items; items = items->next) - { - GtkIconViewItem *item = items->data; - - if (!item->selected) - { - dirty = TRUE; - item->selected = TRUE; - gtk_icon_view_queue_draw_item (icon_view, item); - } - } - - if (dirty) - g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0); -} - -/** - * gtk_icon_view_unselect_all: - * @icon_view: A `GtkIconView`. - * - * Unselects all the icons. - **/ -void -gtk_icon_view_unselect_all (GtkIconView *icon_view) -{ - gboolean dirty = FALSE; - - g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); - - if (icon_view->priv->selection_mode == GTK_SELECTION_BROWSE) - return; - - dirty = gtk_icon_view_unselect_all_internal (icon_view); - - if (dirty) - g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0); -} - -/** - * gtk_icon_view_path_is_selected: - * @icon_view: A `GtkIconView`. - * @path: A `GtkTreePath` to check selection on. - * - * Returns %TRUE if the icon pointed to by @path is currently - * selected. If @path does not point to a valid location, %FALSE is returned. - * - * Returns: %TRUE if @path is selected. - **/ -gboolean -gtk_icon_view_path_is_selected (GtkIconView *icon_view, - GtkTreePath *path) -{ - GtkIconViewItem *item; - - g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), FALSE); - g_return_val_if_fail (icon_view->priv->model != NULL, FALSE); - g_return_val_if_fail (path != NULL, FALSE); - - item = g_list_nth_data (icon_view->priv->items, - gtk_tree_path_get_indices(path)[0]); - - if (!item) - return FALSE; - - return item->selected; -} - -/** - * gtk_icon_view_get_item_row: - * @icon_view: a `GtkIconView` - * @path: the `GtkTreePath` of the item - * - * Gets the row in which the item @path is currently - * displayed. Row numbers start at 0. - * - * Returns: The row in which the item is displayed - */ -int -gtk_icon_view_get_item_row (GtkIconView *icon_view, - GtkTreePath *path) -{ - GtkIconViewItem *item; - - g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), -1); - g_return_val_if_fail (icon_view->priv->model != NULL, -1); - g_return_val_if_fail (path != NULL, -1); - - item = g_list_nth_data (icon_view->priv->items, - gtk_tree_path_get_indices(path)[0]); - - if (!item) - return -1; - - return item->row; -} - -/** - * gtk_icon_view_get_item_column: - * @icon_view: a `GtkIconView` - * @path: the `GtkTreePath` of the item - * - * Gets the column in which the item @path is currently - * displayed. Column numbers start at 0. - * - * Returns: The column in which the item is displayed - */ -int -gtk_icon_view_get_item_column (GtkIconView *icon_view, - GtkTreePath *path) -{ - GtkIconViewItem *item; - - g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), -1); - g_return_val_if_fail (icon_view->priv->model != NULL, -1); - g_return_val_if_fail (path != NULL, -1); - - item = g_list_nth_data (icon_view->priv->items, - gtk_tree_path_get_indices(path)[0]); - - if (!item) - return -1; - - return item->col; -} - -/** - * gtk_icon_view_item_activated: - * @icon_view: A `GtkIconView` - * @path: The `GtkTreePath` to be activated - * - * Activates the item determined by @path. - **/ -void -gtk_icon_view_item_activated (GtkIconView *icon_view, - GtkTreePath *path) -{ - g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); - g_return_if_fail (path != NULL); - - g_signal_emit (icon_view, icon_view_signals[ITEM_ACTIVATED], 0, path); -} - -/** - * gtk_icon_view_set_item_orientation: - * @icon_view: a `GtkIconView` - * @orientation: the relative position of texts and icons - * - * Sets the ::item-orientation property which determines whether the labels - * are drawn beside the icons instead of below. - **/ -void -gtk_icon_view_set_item_orientation (GtkIconView *icon_view, - GtkOrientation orientation) -{ - g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); - - if (icon_view->priv->item_orientation != orientation) - { - icon_view->priv->item_orientation = orientation; - - if (icon_view->priv->cell_area) - { - if (GTK_IS_ORIENTABLE (icon_view->priv->cell_area)) - gtk_orientable_set_orientation (GTK_ORIENTABLE (icon_view->priv->cell_area), - icon_view->priv->item_orientation); - - gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); - } - - gtk_icon_view_invalidate_sizes (icon_view); - - update_text_cell (icon_view); - update_pixbuf_cell (icon_view); - - g_object_notify (G_OBJECT (icon_view), "item-orientation"); - } -} - -/** - * gtk_icon_view_get_item_orientation: - * @icon_view: a `GtkIconView` - * - * Returns the value of the ::item-orientation property which determines - * whether the labels are drawn beside the icons instead of below. - * - * Returns: the relative position of texts and icons - **/ -GtkOrientation -gtk_icon_view_get_item_orientation (GtkIconView *icon_view) -{ - g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), - GTK_ORIENTATION_VERTICAL); - - return icon_view->priv->item_orientation; -} - -/** - * gtk_icon_view_set_columns: - * @icon_view: a `GtkIconView` - * @columns: the number of columns - * - * Sets the ::columns property which determines in how - * many columns the icons are arranged. If @columns is - * -1, the number of columns will be chosen automatically - * to fill the available area. - */ -void -gtk_icon_view_set_columns (GtkIconView *icon_view, - int columns) -{ - g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); - - if (icon_view->priv->columns != columns) - { - icon_view->priv->columns = columns; - - if (icon_view->priv->cell_area) - gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); - - gtk_widget_queue_resize (GTK_WIDGET (icon_view)); - - g_object_notify (G_OBJECT (icon_view), "columns"); - } -} - -/** - * gtk_icon_view_get_columns: - * @icon_view: a `GtkIconView` - * - * Returns the value of the ::columns property. - * - * Returns: the number of columns, or -1 - */ -int -gtk_icon_view_get_columns (GtkIconView *icon_view) -{ - g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), -1); - - return icon_view->priv->columns; -} - -/** - * gtk_icon_view_set_item_width: - * @icon_view: a `GtkIconView` - * @item_width: the width for each item - * - * Sets the ::item-width property which specifies the width - * to use for each item. If it is set to -1, the icon view will - * automatically determine a suitable item size. - */ -void -gtk_icon_view_set_item_width (GtkIconView *icon_view, - int item_width) -{ - g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); - - if (icon_view->priv->item_width != item_width) - { - icon_view->priv->item_width = item_width; - - if (icon_view->priv->cell_area) - gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); - - gtk_icon_view_invalidate_sizes (icon_view); - - update_text_cell (icon_view); - - g_object_notify (G_OBJECT (icon_view), "item-width"); - } -} - -/** - * gtk_icon_view_get_item_width: - * @icon_view: a `GtkIconView` - * - * Returns the value of the ::item-width property. - * - * Returns: the width of a single item, or -1 - */ -int -gtk_icon_view_get_item_width (GtkIconView *icon_view) -{ - g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), -1); - - return icon_view->priv->item_width; -} - - -/** - * gtk_icon_view_set_spacing: - * @icon_view: a `GtkIconView` - * @spacing: the spacing - * - * Sets the ::spacing property which specifies the space - * which is inserted between the cells (i.e. the icon and - * the text) of an item. - */ -void -gtk_icon_view_set_spacing (GtkIconView *icon_view, - int spacing) -{ - g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); - - if (icon_view->priv->spacing != spacing) - { - icon_view->priv->spacing = spacing; - - if (icon_view->priv->cell_area) - gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); - - gtk_icon_view_invalidate_sizes (icon_view); - - g_object_notify (G_OBJECT (icon_view), "spacing"); - } -} - -/** - * gtk_icon_view_get_spacing: - * @icon_view: a `GtkIconView` - * - * Returns the value of the ::spacing property. - * - * Returns: the space between cells - */ -int -gtk_icon_view_get_spacing (GtkIconView *icon_view) -{ - g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), -1); - - return icon_view->priv->spacing; -} - -/** - * gtk_icon_view_set_row_spacing: - * @icon_view: a `GtkIconView` - * @row_spacing: the row spacing - * - * Sets the ::row-spacing property which specifies the space - * which is inserted between the rows of the icon view. - */ -void -gtk_icon_view_set_row_spacing (GtkIconView *icon_view, - int row_spacing) -{ - g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); - - if (icon_view->priv->row_spacing != row_spacing) - { - icon_view->priv->row_spacing = row_spacing; - - if (icon_view->priv->cell_area) - gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); - - gtk_icon_view_invalidate_sizes (icon_view); - - g_object_notify (G_OBJECT (icon_view), "row-spacing"); - } -} - -/** - * gtk_icon_view_get_row_spacing: - * @icon_view: a `GtkIconView` - * - * Returns the value of the ::row-spacing property. - * - * Returns: the space between rows - */ -int -gtk_icon_view_get_row_spacing (GtkIconView *icon_view) -{ - g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), -1); - - return icon_view->priv->row_spacing; -} - -/** - * gtk_icon_view_set_column_spacing: - * @icon_view: a `GtkIconView` - * @column_spacing: the column spacing - * - * Sets the ::column-spacing property which specifies the space - * which is inserted between the columns of the icon view. - */ -void -gtk_icon_view_set_column_spacing (GtkIconView *icon_view, - int column_spacing) -{ - g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); - - if (icon_view->priv->column_spacing != column_spacing) - { - icon_view->priv->column_spacing = column_spacing; - - if (icon_view->priv->cell_area) - gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); - - gtk_icon_view_invalidate_sizes (icon_view); - - g_object_notify (G_OBJECT (icon_view), "column-spacing"); - } -} - -/** - * gtk_icon_view_get_column_spacing: - * @icon_view: a `GtkIconView` - * - * Returns the value of the ::column-spacing property. - * - * Returns: the space between columns - */ -int -gtk_icon_view_get_column_spacing (GtkIconView *icon_view) -{ - g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), -1); - - return icon_view->priv->column_spacing; -} - -/** - * gtk_icon_view_set_margin: - * @icon_view: a `GtkIconView` - * @margin: the margin - * - * Sets the ::margin property which specifies the space - * which is inserted at the top, bottom, left and right - * of the icon view. - */ -void -gtk_icon_view_set_margin (GtkIconView *icon_view, - int margin) -{ - g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); - - if (icon_view->priv->margin != margin) - { - icon_view->priv->margin = margin; - - if (icon_view->priv->cell_area) - gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); - - gtk_icon_view_invalidate_sizes (icon_view); - - g_object_notify (G_OBJECT (icon_view), "margin"); - } -} - -/** - * gtk_icon_view_get_margin: - * @icon_view: a `GtkIconView` - * - * Returns the value of the ::margin property. - * - * Returns: the space at the borders - */ -int -gtk_icon_view_get_margin (GtkIconView *icon_view) -{ - g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), -1); - - return icon_view->priv->margin; -} - -/** - * gtk_icon_view_set_item_padding: - * @icon_view: a `GtkIconView` - * @item_padding: the item padding - * - * Sets the `GtkIconView`:item-padding property which specifies the padding - * around each of the icon view’s items. - */ -void -gtk_icon_view_set_item_padding (GtkIconView *icon_view, - int item_padding) -{ - g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); - - if (icon_view->priv->item_padding != item_padding) - { - icon_view->priv->item_padding = item_padding; - - if (icon_view->priv->cell_area) - gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); - - gtk_icon_view_invalidate_sizes (icon_view); - - g_object_notify (G_OBJECT (icon_view), "item-padding"); - } -} - -/** - * gtk_icon_view_get_item_padding: - * @icon_view: a `GtkIconView` - * - * Returns the value of the ::item-padding property. - * - * Returns: the padding around items - */ -int -gtk_icon_view_get_item_padding (GtkIconView *icon_view) -{ - g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), -1); - - return icon_view->priv->item_padding; -} - -/* Get/set whether drag_motion requested the drag data and - * drag_data_received should thus not actually insert the data, - * since the data doesn’t result from a drop. - */ -static void -set_status_pending (GdkDrop *drop, - GdkDragAction suggested_action) -{ - g_object_set_data (G_OBJECT (drop), - I_("gtk-icon-view-status-pending"), - GINT_TO_POINTER (suggested_action)); -} - -static GdkDragAction -get_status_pending (GdkDrop *drop) -{ - return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (drop), - "gtk-icon-view-status-pending")); -} - -static void -unset_reorderable (GtkIconView *icon_view) -{ - if (icon_view->priv->reorderable) - { - icon_view->priv->reorderable = FALSE; - g_object_notify (G_OBJECT (icon_view), "reorderable"); - } -} - -typedef struct -{ - GtkTreeRowReference *dest_row; - gboolean empty_view_drop; - gboolean drop_append_mode; -} DestRow; - -static void -dest_row_free (gpointer data) -{ - DestRow *dr = (DestRow *)data; - - gtk_tree_row_reference_free (dr->dest_row); - g_free (dr); -} - -static void -set_dest_row (GdkDrop *drop, - GtkTreeModel *model, - GtkTreePath *dest_row, - gboolean empty_view_drop, - gboolean drop_append_mode) -{ - DestRow *dr; - - if (!dest_row) - { - g_object_set_data_full (G_OBJECT (drop), - I_("gtk-icon-view-dest-row"), - NULL, NULL); - return; - } - - dr = g_new0 (DestRow, 1); - - dr->dest_row = gtk_tree_row_reference_new (model, dest_row); - dr->empty_view_drop = empty_view_drop; - dr->drop_append_mode = drop_append_mode; - g_object_set_data_full (G_OBJECT (drop), - I_("gtk-icon-view-dest-row"), - dr, (GDestroyNotify) dest_row_free); -} - -static GtkTreePath* -get_dest_row (GdkDrop *drop) -{ - DestRow *dr; - - dr = g_object_get_data (G_OBJECT (drop), "gtk-icon-view-dest-row"); - - if (dr) - { - GtkTreePath *path = NULL; - - if (dr->dest_row) - path = gtk_tree_row_reference_get_path (dr->dest_row); - else if (dr->empty_view_drop) - path = gtk_tree_path_new_from_indices (0, -1); - else - path = NULL; - - if (path && dr->drop_append_mode) - gtk_tree_path_next (path); - - return path; - } - else - return NULL; -} - -static gboolean -check_model_dnd (GtkTreeModel *model, - GType required_iface, - const char *signal) -{ - if (model == NULL || !G_TYPE_CHECK_INSTANCE_TYPE ((model), required_iface)) - { - g_warning ("You must override the default '%s' handler " - "on GtkIconView when using models that don't support " - "the %s interface and enabling drag-and-drop. The simplest way to do this " - "is to connect to '%s' and call " - "g_signal_stop_emission_by_name() in your signal handler to prevent " - "the default handler from running. Look at the source code " - "for the default handler in gtkiconview.c to get an idea what " - "your handler should do. (gtkiconview.c is in the GTK source " - "code.) If you're using GTK from a language other than C, " - "there may be a more natural way to override default handlers, e.g. via derivation.", - signal, g_type_name (required_iface), signal); - return FALSE; - } - else - return TRUE; -} - -static void -remove_scroll_timeout (GtkIconView *icon_view) -{ - if (icon_view->priv->scroll_timeout_id != 0) - { - g_source_remove (icon_view->priv->scroll_timeout_id); - - icon_view->priv->scroll_timeout_id = 0; - } -} - -static void -gtk_icon_view_autoscroll (GtkIconView *icon_view) -{ - int px, py, width, height; - int hoffset, voffset; - - px = icon_view->priv->event_last_x; - py = icon_view->priv->event_last_y; - - width = gtk_widget_get_width (GTK_WIDGET (icon_view)); - height = gtk_widget_get_height (GTK_WIDGET (icon_view)); - - /* see if we are near the edge. */ - voffset = py - 2 * SCROLL_EDGE_SIZE; - if (voffset > 0) - voffset = MAX (py - (height - 2 * SCROLL_EDGE_SIZE), 0); - - hoffset = px - 2 * SCROLL_EDGE_SIZE; - if (hoffset > 0) - hoffset = MAX (px - (width - 2 * SCROLL_EDGE_SIZE), 0); - - if (voffset != 0) - gtk_adjustment_set_value (icon_view->priv->vadjustment, - gtk_adjustment_get_value (icon_view->priv->vadjustment) + voffset); - - if (hoffset != 0) - gtk_adjustment_set_value (icon_view->priv->hadjustment, - gtk_adjustment_get_value (icon_view->priv->hadjustment) + hoffset); -} - -static gboolean -drag_scroll_timeout (gpointer data) -{ - gtk_icon_view_autoscroll (data); - - return TRUE; -} - -static GdkDragAction -gtk_icon_view_get_action (GtkWidget *widget, - GdkDrop *drop) -{ - GtkIconView *iconview = GTK_ICON_VIEW (widget); - GdkDrag *drag = gdk_drop_get_drag (drop); - GdkDragAction actions; - - actions = gdk_drop_get_actions (drop); - - if (drag == iconview->priv->drag && - actions & GDK_ACTION_MOVE) - return GDK_ACTION_MOVE; - - if (actions & GDK_ACTION_COPY) - return GDK_ACTION_COPY; - - if (actions & GDK_ACTION_MOVE) - return GDK_ACTION_MOVE; - - if (actions & GDK_ACTION_LINK) - return GDK_ACTION_LINK; - - return 0; -} - -static gboolean -set_destination (GtkIconView *icon_view, - GdkDrop *drop, - GtkDropTargetAsync *dest, - int x, - int y, - GdkDragAction *suggested_action, - GType *target) -{ - GtkWidget *widget; - GtkTreePath *path = NULL; - GtkIconViewDropPosition pos; - GtkIconViewDropPosition old_pos; - GtkTreePath *old_dest_path = NULL; - GdkContentFormats *formats; - gboolean can_drop = FALSE; - - widget = GTK_WIDGET (icon_view); - - *suggested_action = 0; - *target = G_TYPE_INVALID; - - if (!icon_view->priv->dest_set) - { - /* someone unset us as a drag dest, note that if - * we return FALSE drag_leave isn't called - */ - - gtk_icon_view_set_drag_dest_item (icon_view, - NULL, - GTK_ICON_VIEW_DROP_LEFT); - - remove_scroll_timeout (GTK_ICON_VIEW (widget)); - - return FALSE; /* no longer a drop site */ - } - - formats = gtk_drop_target_async_get_formats (dest); - *target = gdk_content_formats_match_gtype (formats, formats); - if (*target == G_TYPE_INVALID) - return FALSE; - - if (!gtk_icon_view_get_dest_item_at_pos (icon_view, x, y, &path, &pos)) - { - int n_children; - GtkTreeModel *model; - - /* the row got dropped on empty space, let's setup a special case - */ - - if (path) - gtk_tree_path_free (path); - - model = gtk_icon_view_get_model (icon_view); - - n_children = gtk_tree_model_iter_n_children (model, NULL); - if (n_children) - { - pos = GTK_ICON_VIEW_DROP_BELOW; - path = gtk_tree_path_new_from_indices (n_children - 1, -1); - } - else - { - pos = GTK_ICON_VIEW_DROP_ABOVE; - path = gtk_tree_path_new_from_indices (0, -1); - } - - can_drop = TRUE; - - goto out; - } - - g_assert (path); - - gtk_icon_view_get_drag_dest_item (icon_view, - &old_dest_path, - &old_pos); - - if (old_dest_path) - gtk_tree_path_free (old_dest_path); - - if (TRUE /* FIXME if the location droppable predicate */) - { - can_drop = TRUE; - } - -out: - if (can_drop) - { - *suggested_action = gtk_icon_view_get_action (widget, drop); - - gtk_icon_view_set_drag_dest_item (GTK_ICON_VIEW (widget), - path, pos); - } - else - { - /* can't drop here */ - gtk_icon_view_set_drag_dest_item (GTK_ICON_VIEW (widget), - NULL, - GTK_ICON_VIEW_DROP_LEFT); - } - - if (path) - gtk_tree_path_free (path); - - return TRUE; -} - -static GtkTreePath* -get_logical_destination (GtkIconView *icon_view, - gboolean *drop_append_mode) -{ - /* adjust path to point to the row the drop goes in front of */ - GtkTreePath *path = NULL; - GtkIconViewDropPosition pos; - - *drop_append_mode = FALSE; - - gtk_icon_view_get_drag_dest_item (icon_view, &path, &pos); - - if (path == NULL) - return NULL; - - if (pos == GTK_ICON_VIEW_DROP_RIGHT || - pos == GTK_ICON_VIEW_DROP_BELOW) - { - GtkTreeIter iter; - GtkTreeModel *model = icon_view->priv->model; - - if (!gtk_tree_model_get_iter (model, &iter, path) || - !gtk_tree_model_iter_next (model, &iter)) - *drop_append_mode = TRUE; - else - { - *drop_append_mode = FALSE; - gtk_tree_path_next (path); - } - } - - return path; -} - -static gboolean -gtk_icon_view_maybe_begin_drag (GtkIconView *icon_view, - double x, - double y, - GdkDevice *device) -{ - GtkTreePath *path = NULL; - GtkTreeModel *model; - gboolean retval = FALSE; - GdkContentProvider *content; - GdkPaintable *icon; - GtkIconViewItem *item; - GdkSurface *surface; - GdkDrag *drag; - - if (!icon_view->priv->source_set) - goto out; - - if (icon_view->priv->pressed_button < 0) - goto out; - - if (!gtk_drag_check_threshold_double (GTK_WIDGET (icon_view), - icon_view->priv->press_start_x, - icon_view->priv->press_start_y, - x, y)) - goto out; - - model = gtk_icon_view_get_model (icon_view); - - if (model == NULL) - goto out; - - icon_view->priv->pressed_button = -1; - - item = _gtk_icon_view_get_item_at_coords (icon_view, - icon_view->priv->press_start_x, - icon_view->priv->press_start_y, - TRUE, - NULL); - - if (item == NULL) - goto out; - - path = gtk_tree_path_new_from_indices (item->index, -1); - - if (!GTK_IS_TREE_DRAG_SOURCE (model) || - !gtk_tree_drag_source_row_draggable (GTK_TREE_DRAG_SOURCE (model), path)) - goto out; - - /* FIXME Check whether we're a start button, if not return FALSE and - * free path - */ - - /* Now we can begin the drag */ - - retval = TRUE; - - surface = gtk_native_get_surface (gtk_widget_get_native (GTK_WIDGET (icon_view))); - - content = gtk_icon_view_drag_data_get (icon_view, path); - if (content == NULL) - goto out; - - drag = gdk_drag_begin (surface, - device, - content, - icon_view->priv->source_actions, - icon_view->priv->press_start_x, - icon_view->priv->press_start_y); - - g_object_unref (content); - - g_signal_connect (drag, "dnd-finished", G_CALLBACK (gtk_icon_view_dnd_finished_cb), icon_view); - - icon_view->priv->source_item = gtk_tree_row_reference_new (model, path); - - x = icon_view->priv->press_start_x - item->cell_area.x + icon_view->priv->item_padding; - y = icon_view->priv->press_start_y - item->cell_area.y + icon_view->priv->item_padding; - - icon = gtk_icon_view_create_drag_icon (icon_view, path); - gtk_drag_icon_set_from_paintable (drag, icon, x, y); - g_object_unref (icon); - - icon_view->priv->drag = drag; - - g_object_unref (drag); - - out: - if (path) - gtk_tree_path_free (path); - - return retval; -} - -/* Source side drag signals */ -static GdkContentProvider * -gtk_icon_view_drag_data_get (GtkIconView *icon_view, - GtkTreePath *source_row) -{ - GdkContentProvider *content; - GtkTreeModel *model; - - model = gtk_icon_view_get_model (icon_view); - - if (model == NULL) - return NULL; - - if (!icon_view->priv->source_set) - return NULL; - - /* We can implement the GTK_TREE_MODEL_ROW target generically for - * any model; for DragSource models there are some other formats - * we also support. - */ - - if (GTK_IS_TREE_DRAG_SOURCE (model)) - content = gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (model), source_row); - else - content = NULL; - - /* If drag_data_get does nothing, try providing row data. */ - if (content == NULL) - content = gtk_tree_create_row_drag_content (model, source_row); - - return content; -} - -static void -gtk_icon_view_dnd_finished_cb (GdkDrag *drag, - GtkWidget *widget) -{ - GtkTreeModel *model; - GtkIconView *icon_view; - GtkTreePath *source_row; - - if (gdk_drag_get_selected_action (drag) != GDK_ACTION_MOVE) - return; - - icon_view = GTK_ICON_VIEW (widget); - model = gtk_icon_view_get_model (icon_view); - - if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_SOURCE, "drag-data-delete")) - return; - - if (!icon_view->priv->source_set) - return; - - source_row = gtk_tree_row_reference_get_path (icon_view->priv->source_item); - if (source_row == NULL) - return; - - gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (model), source_row); - - gtk_tree_path_free (source_row); - - g_clear_pointer (&icon_view->priv->source_item, gtk_tree_row_reference_free); - icon_view->priv->drag = NULL; -} - -/* Target side drag signals */ -static void -gtk_icon_view_drag_leave (GtkDropTargetAsync *dest, - GdkDrop *drop, - GtkIconView *icon_view) -{ - /* unset any highlight row */ - gtk_icon_view_set_drag_dest_item (icon_view, - NULL, - GTK_ICON_VIEW_DROP_LEFT); - - remove_scroll_timeout (icon_view); -} - -static GdkDragAction -gtk_icon_view_drag_motion (GtkDropTargetAsync *dest, - GdkDrop *drop, - double x, - double y, - GtkIconView *icon_view) -{ - GtkTreePath *path = NULL; - GtkIconViewDropPosition pos; - GdkDragAction suggested_action = 0; - GType target; - gboolean empty; - GdkDragAction result; - - if (!set_destination (icon_view, drop, dest, x, y, &suggested_action, &target)) - return 0; - - gtk_icon_view_get_drag_dest_item (icon_view, &path, &pos); - - /* we only know this *after* set_desination_row */ - empty = icon_view->priv->empty_view_drop; - - if (path == NULL && !empty) - { - /* Can't drop here. */ - result = 0; - } - else - { - if (icon_view->priv->scroll_timeout_id == 0) - { - icon_view->priv->scroll_timeout_id = g_timeout_add (50, drag_scroll_timeout, icon_view); - gdk_source_set_static_name_by_id (icon_view->priv->scroll_timeout_id, "[gtk] drag_scroll_timeout"); - } - - if (target == GTK_TYPE_TREE_ROW_DATA) - { - /* Request data so we can use the source row when - * determining whether to accept the drop - */ - set_status_pending (drop, suggested_action); - gdk_drop_read_value_async (drop, target, G_PRIORITY_DEFAULT, NULL, gtk_icon_view_drag_data_received, icon_view); - } - else - { - set_status_pending (drop, 0); - } - result = suggested_action; - } - - if (path) - gtk_tree_path_free (path); - - return result; -} - -static gboolean -gtk_icon_view_drag_drop (GtkDropTargetAsync *dest, - GdkDrop *drop, - double x, - double y, - GtkIconView *icon_view) -{ - GtkTreePath *path; - GdkDragAction suggested_action = 0; - GType target = G_TYPE_INVALID; - GtkTreeModel *model; - gboolean drop_append_mode; - - model = gtk_icon_view_get_model (icon_view); - - remove_scroll_timeout (icon_view); - - if (!icon_view->priv->dest_set) - return FALSE; - - if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drop")) - return FALSE; - - if (!set_destination (icon_view, drop, dest, x, y, &suggested_action, &target)) - return FALSE; - - path = get_logical_destination (icon_view, &drop_append_mode); - - if (target != G_TYPE_INVALID && path != NULL) - { - /* in case a motion had requested drag data, change things so we - * treat drag data receives as a drop. - */ - set_status_pending (drop, 0); - set_dest_row (drop, model, path, - icon_view->priv->empty_view_drop, drop_append_mode); - } - - if (path) - gtk_tree_path_free (path); - - /* Unset this thing */ - gtk_icon_view_set_drag_dest_item (icon_view, NULL, GTK_ICON_VIEW_DROP_LEFT); - - if (target != G_TYPE_INVALID) - { - gdk_drop_read_value_async (drop, target, G_PRIORITY_DEFAULT, NULL, gtk_icon_view_drag_data_received, icon_view); - return TRUE; - } - else - return FALSE; -} - -static void -gtk_icon_view_drag_data_received (GObject *source, - GAsyncResult *result, - gpointer data) -{ - GtkIconView *icon_view = data; - GdkDrop *drop = GDK_DROP (source); - GtkTreePath *path; - GtkTreeModel *model; - GtkTreePath *dest_row; - GdkDragAction suggested_action; - gboolean drop_append_mode; - const GValue *value; - - value = gdk_drop_read_value_finish (drop, result, NULL); - if (!value) - return; - - model = gtk_icon_view_get_model (icon_view); - - if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag-data-received")) - return; - - if (!icon_view->priv->dest_set) - return; - - suggested_action = get_status_pending (drop); - - if (suggested_action) - { - /* We are getting this data due to a request in drag_motion, - * rather than due to a request in drag_drop, so we are just - * supposed to call drag_status, not actually paste in the - * data. - */ - path = get_logical_destination (icon_view, &drop_append_mode); - - if (path == NULL) - suggested_action = 0; - - if (suggested_action) - { - if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model), - path, - value)) - suggested_action = 0; - } - - if (path) - gtk_tree_path_free (path); - - /* If you can't drop, remove user drop indicator until the next motion */ - if (suggested_action == 0) - gtk_icon_view_set_drag_dest_item (icon_view, - NULL, - GTK_ICON_VIEW_DROP_LEFT); - return; - } - - - dest_row = get_dest_row (drop); - - if (dest_row == NULL) - return; - - suggested_action = gtk_icon_view_get_action (GTK_WIDGET (icon_view), drop); - - if (suggested_action && - !gtk_tree_drag_dest_drag_data_received (GTK_TREE_DRAG_DEST (model), - dest_row, - value)) - suggested_action = 0; - - gdk_drop_finish (drop, suggested_action); - - gtk_tree_path_free (dest_row); - - /* drop dest_row */ - set_dest_row (drop, NULL, NULL, FALSE, FALSE); -} - -/* Drag-and-Drop support */ - -/** - * gtk_icon_view_enable_model_drag_source: - * @icon_view: a `GtkIconView` - * @start_button_mask: Mask of allowed buttons to start drag - * @formats: the formats that the drag will support - * @actions: the bitmask of possible actions for a drag from this - * widget - * - * Turns @icon_view into a drag source for automatic DND. Calling this - * method sets `GtkIconView`:reorderable to %FALSE. - **/ -void -gtk_icon_view_enable_model_drag_source (GtkIconView *icon_view, - GdkModifierType start_button_mask, - GdkContentFormats *formats, - GdkDragAction actions) -{ - g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); - - icon_view->priv->source_formats = gdk_content_formats_ref (formats); - icon_view->priv->source_actions = actions; - - icon_view->priv->source_set = TRUE; - - unset_reorderable (icon_view); -} - -/** - * gtk_icon_view_enable_model_drag_dest: - * @icon_view: a `GtkIconView` - * @formats: the formats that the drag will support - * @actions: the bitmask of possible actions for a drag to this - * widget - * - * Turns @icon_view into a drop destination for automatic DND. Calling this - * method sets `GtkIconView`:reorderable to %FALSE. - **/ -void -gtk_icon_view_enable_model_drag_dest (GtkIconView *icon_view, - GdkContentFormats *formats, - GdkDragAction actions) -{ - GtkCssNode *widget_node; - - g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); - - icon_view->priv->dest = gtk_drop_target_async_new (gdk_content_formats_ref (formats), actions); - g_signal_connect (icon_view->priv->dest, "drag-leave", G_CALLBACK (gtk_icon_view_drag_leave), icon_view); - g_signal_connect (icon_view->priv->dest, "drag-enter", G_CALLBACK (gtk_icon_view_drag_motion), icon_view); - g_signal_connect (icon_view->priv->dest, "drag-motion", G_CALLBACK (gtk_icon_view_drag_motion), icon_view); - g_signal_connect (icon_view->priv->dest, "drop", G_CALLBACK (gtk_icon_view_drag_drop), icon_view); - gtk_widget_add_controller (GTK_WIDGET (icon_view), GTK_EVENT_CONTROLLER (icon_view->priv->dest)); - - icon_view->priv->dest_actions = actions; - - icon_view->priv->dest_set = TRUE; - - unset_reorderable (icon_view); - - widget_node = gtk_widget_get_css_node (GTK_WIDGET (icon_view)); - icon_view->priv->dndnode = gtk_css_node_new (); - gtk_css_node_set_name (icon_view->priv->dndnode, g_quark_from_static_string ("dndtarget")); - gtk_css_node_set_parent (icon_view->priv->dndnode, widget_node); - gtk_css_node_set_state (icon_view->priv->dndnode, gtk_css_node_get_state (widget_node)); - g_object_unref (icon_view->priv->dndnode); -} - -/** - * gtk_icon_view_unset_model_drag_source: - * @icon_view: a `GtkIconView` - * - * Undoes the effect of gtk_icon_view_enable_model_drag_source(). Calling this - * method sets `GtkIconView`:reorderable to %FALSE. - **/ -void -gtk_icon_view_unset_model_drag_source (GtkIconView *icon_view) -{ - g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); - - if (icon_view->priv->source_set) - { - g_clear_pointer (&icon_view->priv->source_formats, gdk_content_formats_unref); - icon_view->priv->source_set = FALSE; - } - - unset_reorderable (icon_view); -} - -/** - * gtk_icon_view_unset_model_drag_dest: - * @icon_view: a `GtkIconView` - * - * Undoes the effect of gtk_icon_view_enable_model_drag_dest(). Calling this - * method sets `GtkIconView`:reorderable to %FALSE. - **/ -void -gtk_icon_view_unset_model_drag_dest (GtkIconView *icon_view) -{ - g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); - - if (icon_view->priv->dest_set) - { - gtk_widget_remove_controller (GTK_WIDGET (icon_view), GTK_EVENT_CONTROLLER (icon_view->priv->dest)); - icon_view->priv->dest = NULL; - icon_view->priv->dest_set = FALSE; - - gtk_css_node_set_parent (icon_view->priv->dndnode, NULL); - icon_view->priv->dndnode = NULL; - } - - unset_reorderable (icon_view); -} - -/* These are useful to implement your own custom stuff. */ -/** - * gtk_icon_view_set_drag_dest_item: - * @icon_view: a `GtkIconView` - * @path: (nullable): The path of the item to highlight - * @pos: Specifies where to drop, relative to the item - * - * Sets the item that is highlighted for feedback. - */ -void -gtk_icon_view_set_drag_dest_item (GtkIconView *icon_view, - GtkTreePath *path, - GtkIconViewDropPosition pos) -{ - /* Note; this function is exported to allow a custom DND - * implementation, so it can't touch TreeViewDragInfo - */ - - g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); - - if (icon_view->priv->dest_item) - { - GtkTreePath *current_path; - current_path = gtk_tree_row_reference_get_path (icon_view->priv->dest_item); - gtk_tree_row_reference_free (icon_view->priv->dest_item); - icon_view->priv->dest_item = NULL; - - gtk_icon_view_queue_draw_path (icon_view, current_path); - gtk_tree_path_free (current_path); - } - - /* special case a drop on an empty model */ - icon_view->priv->empty_view_drop = FALSE; - if (pos == GTK_ICON_VIEW_DROP_ABOVE && path - && gtk_tree_path_get_depth (path) == 1 - && gtk_tree_path_get_indices (path)[0] == 0) - { - int n_children; - - n_children = gtk_tree_model_iter_n_children (icon_view->priv->model, - NULL); - - if (n_children == 0) - icon_view->priv->empty_view_drop = TRUE; - } - - icon_view->priv->dest_pos = pos; - - if (path) - { - icon_view->priv->dest_item = - gtk_tree_row_reference_new_proxy (G_OBJECT (icon_view), - icon_view->priv->model, path); - - gtk_icon_view_queue_draw_path (icon_view, path); - } -} - -/** - * gtk_icon_view_get_drag_dest_item: - * @icon_view: a `GtkIconView` - * @path: (out) (nullable) (optional): Return location for the path of - * the highlighted item - * @pos: (out) (optional): Return location for the drop position - * - * Gets information about the item that is highlighted for feedback. - */ -void -gtk_icon_view_get_drag_dest_item (GtkIconView *icon_view, - GtkTreePath **path, - GtkIconViewDropPosition *pos) -{ - g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); - - if (path) - { - if (icon_view->priv->dest_item) - *path = gtk_tree_row_reference_get_path (icon_view->priv->dest_item); - else - *path = NULL; - } - - if (pos) - *pos = icon_view->priv->dest_pos; -} - -/** - * gtk_icon_view_get_dest_item_at_pos: - * @icon_view: a `GtkIconView` - * @drag_x: the position to determine the destination item for - * @drag_y: the position to determine the destination item for - * @path: (out) (optional): Return location for the path of the item - * @pos: (out) (optional): Return location for the drop position - * - * Determines the destination item for a given position. - * - * Returns: whether there is an item at the given position. - **/ -gboolean -gtk_icon_view_get_dest_item_at_pos (GtkIconView *icon_view, - int drag_x, - int drag_y, - GtkTreePath **path, - GtkIconViewDropPosition *pos) -{ - GtkIconViewItem *item; - - /* Note; this function is exported to allow a custom DND - * implementation, so it can't touch TreeViewDragInfo - */ - - g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), FALSE); - g_return_val_if_fail (drag_x >= 0, FALSE); - g_return_val_if_fail (drag_y >= 0, FALSE); - - - if (path) - *path = NULL; - - item = _gtk_icon_view_get_item_at_coords (icon_view, - drag_x + gtk_adjustment_get_value (icon_view->priv->hadjustment), - drag_y + gtk_adjustment_get_value (icon_view->priv->vadjustment), - FALSE, NULL); - - if (item == NULL) - return FALSE; - - if (path) - *path = gtk_tree_path_new_from_indices (item->index, -1); - - if (pos) - { - if (drag_x < item->cell_area.x + item->cell_area.width / 4) - *pos = GTK_ICON_VIEW_DROP_LEFT; - else if (drag_x > item->cell_area.x + item->cell_area.width * 3 / 4) - *pos = GTK_ICON_VIEW_DROP_RIGHT; - else if (drag_y < item->cell_area.y + item->cell_area.height / 4) - *pos = GTK_ICON_VIEW_DROP_ABOVE; - else if (drag_y > item->cell_area.y + item->cell_area.height * 3 / 4) - *pos = GTK_ICON_VIEW_DROP_BELOW; - else - *pos = GTK_ICON_VIEW_DROP_INTO; - } - - return TRUE; -} - -/** - * gtk_icon_view_create_drag_icon: - * @icon_view: a `GtkIconView` - * @path: a `GtkTreePath` in @icon_view - * - * Creates a `GdkPaintable` representation of the item at @path. - * This image is used for a drag icon. - * - * Returns: (transfer full) (nullable): a newly-allocated `GdkPaintable` of the drag icon. - **/ -GdkPaintable * -gtk_icon_view_create_drag_icon (GtkIconView *icon_view, - GtkTreePath *path) -{ - GtkWidget *widget; - GtkSnapshot *snapshot; - GdkPaintable *paintable; - GList *l; - int index; - - g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), NULL); - g_return_val_if_fail (path != NULL, NULL); - - widget = GTK_WIDGET (icon_view); - - if (!gtk_widget_get_realized (widget)) - return NULL; - - index = gtk_tree_path_get_indices (path)[0]; - - for (l = icon_view->priv->items; l; l = l->next) - { - GtkIconViewItem *item = l->data; - - if (index == item->index) - { - snapshot = gtk_snapshot_new (); - gtk_icon_view_snapshot_item (icon_view, snapshot, item, - icon_view->priv->item_padding, - icon_view->priv->item_padding, - FALSE); - paintable = gtk_snapshot_free_to_paintable (snapshot, NULL); - - return paintable; - } - } - - return NULL; -} - -/** - * gtk_icon_view_get_reorderable: - * @icon_view: a `GtkIconView` - * - * Retrieves whether the user can reorder the list via drag-and-drop. - * See gtk_icon_view_set_reorderable(). - * - * Returns: %TRUE if the list can be reordered. - **/ -gboolean -gtk_icon_view_get_reorderable (GtkIconView *icon_view) -{ - g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), FALSE); - - return icon_view->priv->reorderable; -} - -/** - * gtk_icon_view_set_reorderable: - * @icon_view: A `GtkIconView`. - * @reorderable: %TRUE, if the list of items can be reordered. - * - * This function is a convenience function to allow you to reorder models that - * support the `GtkTreeDragSourceIface` and the `GtkTreeDragDestIface`. Both - * `GtkTreeStore` and `GtkListStore` support these. If @reorderable is %TRUE, then - * the user can reorder the model by dragging and dropping rows. The - * developer can listen to these changes by connecting to the model's - * row_inserted and row_deleted signals. The reordering is implemented by setting up - * the icon view as a drag source and destination. Therefore, drag and - * drop can not be used in a reorderable view for any other purpose. - * - * This function does not give you any degree of control over the order -- any - * reordering is allowed. If more control is needed, you should probably - * handle drag and drop manually. - **/ -void -gtk_icon_view_set_reorderable (GtkIconView *icon_view, - gboolean reorderable) -{ - g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); - - reorderable = reorderable != FALSE; - - if (icon_view->priv->reorderable == reorderable) - return; - - if (reorderable) - { - GdkContentFormats *formats = gdk_content_formats_new_for_gtype (GTK_TYPE_TREE_ROW_DATA); - gtk_icon_view_enable_model_drag_source (icon_view, - GDK_BUTTON1_MASK, - formats, - GDK_ACTION_MOVE); - gtk_icon_view_enable_model_drag_dest (icon_view, - formats, - GDK_ACTION_MOVE); - gdk_content_formats_unref (formats); - } - else - { - gtk_icon_view_unset_model_drag_source (icon_view); - gtk_icon_view_unset_model_drag_dest (icon_view); - } - - icon_view->priv->reorderable = reorderable; - - g_object_notify (G_OBJECT (icon_view), "reorderable"); -} - -/** - * gtk_icon_view_set_activate_on_single_click: - * @icon_view: a `GtkIconView` - * @single: %TRUE to emit item-activated on a single click - * - * Causes the `GtkIconView`::item-activated signal to be emitted on - * a single click instead of a double click. - **/ -void -gtk_icon_view_set_activate_on_single_click (GtkIconView *icon_view, - gboolean single) -{ - g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); - - single = single != FALSE; - - if (icon_view->priv->activate_on_single_click == single) - return; - - icon_view->priv->activate_on_single_click = single; - g_object_notify (G_OBJECT (icon_view), "activate-on-single-click"); -} - -/** - * gtk_icon_view_get_activate_on_single_click: - * @icon_view: a `GtkIconView` - * - * Gets the setting set by gtk_icon_view_set_activate_on_single_click(). - * - * Returns: %TRUE if item-activated will be emitted on a single click - **/ -gboolean -gtk_icon_view_get_activate_on_single_click (GtkIconView *icon_view) -{ - g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), FALSE); - - return icon_view->priv->activate_on_single_click; -} - -static gboolean -gtk_icon_view_buildable_custom_tag_start (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const char *tagname, - GtkBuildableParser *parser, - gpointer *data) -{ - if (parent_buildable_iface->custom_tag_start (buildable, builder, child, - tagname, parser, data)) - return TRUE; - - return _gtk_cell_layout_buildable_custom_tag_start (buildable, builder, child, - tagname, parser, data); -} - -static void -gtk_icon_view_buildable_custom_tag_end (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const char *tagname, - gpointer data) -{ - if (!_gtk_cell_layout_buildable_custom_tag_end (buildable, builder, - child, tagname, data)) - parent_buildable_iface->custom_tag_end (buildable, builder, - child, tagname, data); -} diff --git a/gtk/gtkiconview.h b/gtk/gtkiconview.h deleted file mode 100644 index b16153d809..0000000000 --- a/gtk/gtkiconview.h +++ /dev/null @@ -1,289 +0,0 @@ -/* gtkiconview.h - * Copyright (C) 2002, 2004 Anders Carlsson - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#ifndef __GTK_ICON_VIEW_H__ -#define __GTK_ICON_VIEW_H__ - -#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) -#error "Only can be included directly." -#endif - -#include -#include -#include -#include -#include - -G_BEGIN_DECLS - -#define GTK_TYPE_ICON_VIEW (gtk_icon_view_get_type ()) -#define GTK_ICON_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_ICON_VIEW, GtkIconView)) -#define GTK_IS_ICON_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_ICON_VIEW)) - -typedef struct _GtkIconView GtkIconView; - -/** - * GtkIconViewForeachFunc: - * @icon_view: a `GtkIconView` - * @path: The `GtkTreePath` of a selected row - * @data: (closure): user data - * - * A function used by gtk_icon_view_selected_foreach() to map all - * selected rows. - * - * It will be called on every selected row in the view. - */ -typedef void (* GtkIconViewForeachFunc) (GtkIconView *icon_view, - GtkTreePath *path, - gpointer data); - -/** - * GtkIconViewDropPosition: - * @GTK_ICON_VIEW_NO_DROP: no drop possible - * @GTK_ICON_VIEW_DROP_INTO: dropped item replaces the item - * @GTK_ICON_VIEW_DROP_LEFT: dropped item is inserted to the left - * @GTK_ICON_VIEW_DROP_RIGHT: dropped item is inserted to the right - * @GTK_ICON_VIEW_DROP_ABOVE: dropped item is inserted above - * @GTK_ICON_VIEW_DROP_BELOW: dropped item is inserted below - * - * An enum for determining where a dropped item goes. - */ -typedef enum -{ - GTK_ICON_VIEW_NO_DROP, - GTK_ICON_VIEW_DROP_INTO, - GTK_ICON_VIEW_DROP_LEFT, - GTK_ICON_VIEW_DROP_RIGHT, - GTK_ICON_VIEW_DROP_ABOVE, - GTK_ICON_VIEW_DROP_BELOW -} GtkIconViewDropPosition; - -GDK_AVAILABLE_IN_ALL -GType gtk_icon_view_get_type (void) G_GNUC_CONST; -GDK_AVAILABLE_IN_ALL -GtkWidget * gtk_icon_view_new (void); -GDK_AVAILABLE_IN_ALL -GtkWidget * gtk_icon_view_new_with_area (GtkCellArea *area); -GDK_AVAILABLE_IN_ALL -GtkWidget * gtk_icon_view_new_with_model (GtkTreeModel *model); - -GDK_AVAILABLE_IN_ALL -void gtk_icon_view_set_model (GtkIconView *icon_view, - GtkTreeModel *model); -GDK_AVAILABLE_IN_ALL -GtkTreeModel * gtk_icon_view_get_model (GtkIconView *icon_view); -GDK_AVAILABLE_IN_ALL -void gtk_icon_view_set_text_column (GtkIconView *icon_view, - int column); -GDK_AVAILABLE_IN_ALL -int gtk_icon_view_get_text_column (GtkIconView *icon_view); -GDK_AVAILABLE_IN_ALL -void gtk_icon_view_set_markup_column (GtkIconView *icon_view, - int column); -GDK_AVAILABLE_IN_ALL -int gtk_icon_view_get_markup_column (GtkIconView *icon_view); -GDK_AVAILABLE_IN_ALL -void gtk_icon_view_set_pixbuf_column (GtkIconView *icon_view, - int column); -GDK_AVAILABLE_IN_ALL -int gtk_icon_view_get_pixbuf_column (GtkIconView *icon_view); - -GDK_AVAILABLE_IN_ALL -void gtk_icon_view_set_item_orientation (GtkIconView *icon_view, - GtkOrientation orientation); -GDK_AVAILABLE_IN_ALL -GtkOrientation gtk_icon_view_get_item_orientation (GtkIconView *icon_view); -GDK_AVAILABLE_IN_ALL -void gtk_icon_view_set_columns (GtkIconView *icon_view, - int columns); -GDK_AVAILABLE_IN_ALL -int gtk_icon_view_get_columns (GtkIconView *icon_view); -GDK_AVAILABLE_IN_ALL -void gtk_icon_view_set_item_width (GtkIconView *icon_view, - int item_width); -GDK_AVAILABLE_IN_ALL -int gtk_icon_view_get_item_width (GtkIconView *icon_view); -GDK_AVAILABLE_IN_ALL -void gtk_icon_view_set_spacing (GtkIconView *icon_view, - int spacing); -GDK_AVAILABLE_IN_ALL -int gtk_icon_view_get_spacing (GtkIconView *icon_view); -GDK_AVAILABLE_IN_ALL -void gtk_icon_view_set_row_spacing (GtkIconView *icon_view, - int row_spacing); -GDK_AVAILABLE_IN_ALL -int gtk_icon_view_get_row_spacing (GtkIconView *icon_view); -GDK_AVAILABLE_IN_ALL -void gtk_icon_view_set_column_spacing (GtkIconView *icon_view, - int column_spacing); -GDK_AVAILABLE_IN_ALL -int gtk_icon_view_get_column_spacing (GtkIconView *icon_view); -GDK_AVAILABLE_IN_ALL -void gtk_icon_view_set_margin (GtkIconView *icon_view, - int margin); -GDK_AVAILABLE_IN_ALL -int gtk_icon_view_get_margin (GtkIconView *icon_view); -GDK_AVAILABLE_IN_ALL -void gtk_icon_view_set_item_padding (GtkIconView *icon_view, - int item_padding); -GDK_AVAILABLE_IN_ALL -int gtk_icon_view_get_item_padding (GtkIconView *icon_view); - -GDK_AVAILABLE_IN_ALL -GtkTreePath * gtk_icon_view_get_path_at_pos (GtkIconView *icon_view, - int x, - int y); -GDK_AVAILABLE_IN_ALL -gboolean gtk_icon_view_get_item_at_pos (GtkIconView *icon_view, - int x, - int y, - GtkTreePath **path, - GtkCellRenderer **cell); -GDK_AVAILABLE_IN_ALL -gboolean gtk_icon_view_get_visible_range (GtkIconView *icon_view, - GtkTreePath **start_path, - GtkTreePath **end_path); -GDK_AVAILABLE_IN_ALL -void gtk_icon_view_set_activate_on_single_click (GtkIconView *icon_view, - gboolean single); -GDK_AVAILABLE_IN_ALL -gboolean gtk_icon_view_get_activate_on_single_click (GtkIconView *icon_view); - -GDK_AVAILABLE_IN_ALL -void gtk_icon_view_selected_foreach (GtkIconView *icon_view, - GtkIconViewForeachFunc func, - gpointer data); -GDK_AVAILABLE_IN_ALL -void gtk_icon_view_set_selection_mode (GtkIconView *icon_view, - GtkSelectionMode mode); -GDK_AVAILABLE_IN_ALL -GtkSelectionMode gtk_icon_view_get_selection_mode (GtkIconView *icon_view); -GDK_AVAILABLE_IN_ALL -void gtk_icon_view_select_path (GtkIconView *icon_view, - GtkTreePath *path); -GDK_AVAILABLE_IN_ALL -void gtk_icon_view_unselect_path (GtkIconView *icon_view, - GtkTreePath *path); -GDK_AVAILABLE_IN_ALL -gboolean gtk_icon_view_path_is_selected (GtkIconView *icon_view, - GtkTreePath *path); -GDK_AVAILABLE_IN_ALL -int gtk_icon_view_get_item_row (GtkIconView *icon_view, - GtkTreePath *path); -GDK_AVAILABLE_IN_ALL -int gtk_icon_view_get_item_column (GtkIconView *icon_view, - GtkTreePath *path); -GDK_AVAILABLE_IN_ALL -GList *gtk_icon_view_get_selected_items (GtkIconView *icon_view); -GDK_AVAILABLE_IN_ALL -void gtk_icon_view_select_all (GtkIconView *icon_view); -GDK_AVAILABLE_IN_ALL -void gtk_icon_view_unselect_all (GtkIconView *icon_view); -GDK_AVAILABLE_IN_ALL -void gtk_icon_view_item_activated (GtkIconView *icon_view, - GtkTreePath *path); -GDK_AVAILABLE_IN_ALL -void gtk_icon_view_set_cursor (GtkIconView *icon_view, - GtkTreePath *path, - GtkCellRenderer *cell, - gboolean start_editing); -GDK_AVAILABLE_IN_ALL -gboolean gtk_icon_view_get_cursor (GtkIconView *icon_view, - GtkTreePath **path, - GtkCellRenderer **cell); -GDK_AVAILABLE_IN_ALL -void gtk_icon_view_scroll_to_path (GtkIconView *icon_view, - GtkTreePath *path, - gboolean use_align, - float row_align, - float col_align); - -/* Drag-and-Drop support */ -GDK_AVAILABLE_IN_ALL -void gtk_icon_view_enable_model_drag_source (GtkIconView *icon_view, - GdkModifierType start_button_mask, - GdkContentFormats *formats, - GdkDragAction actions); -GDK_AVAILABLE_IN_ALL -void gtk_icon_view_enable_model_drag_dest (GtkIconView *icon_view, - GdkContentFormats *formats, - GdkDragAction actions); -GDK_AVAILABLE_IN_ALL -void gtk_icon_view_unset_model_drag_source (GtkIconView *icon_view); -GDK_AVAILABLE_IN_ALL -void gtk_icon_view_unset_model_drag_dest (GtkIconView *icon_view); -GDK_AVAILABLE_IN_ALL -void gtk_icon_view_set_reorderable (GtkIconView *icon_view, - gboolean reorderable); -GDK_AVAILABLE_IN_ALL -gboolean gtk_icon_view_get_reorderable (GtkIconView *icon_view); - - -/* These are useful to implement your own custom stuff. */ -GDK_AVAILABLE_IN_ALL -void gtk_icon_view_set_drag_dest_item (GtkIconView *icon_view, - GtkTreePath *path, - GtkIconViewDropPosition pos); -GDK_AVAILABLE_IN_ALL -void gtk_icon_view_get_drag_dest_item (GtkIconView *icon_view, - GtkTreePath **path, - GtkIconViewDropPosition *pos); -GDK_AVAILABLE_IN_ALL -gboolean gtk_icon_view_get_dest_item_at_pos (GtkIconView *icon_view, - int drag_x, - int drag_y, - GtkTreePath **path, - GtkIconViewDropPosition *pos); -GDK_AVAILABLE_IN_ALL -GdkPaintable *gtk_icon_view_create_drag_icon (GtkIconView *icon_view, - GtkTreePath *path); - -GDK_AVAILABLE_IN_ALL -gboolean gtk_icon_view_get_cell_rect (GtkIconView *icon_view, - GtkTreePath *path, - GtkCellRenderer *cell, - GdkRectangle *rect); - - -GDK_AVAILABLE_IN_ALL -void gtk_icon_view_set_tooltip_item (GtkIconView *icon_view, - GtkTooltip *tooltip, - GtkTreePath *path); -GDK_AVAILABLE_IN_ALL -void gtk_icon_view_set_tooltip_cell (GtkIconView *icon_view, - GtkTooltip *tooltip, - GtkTreePath *path, - GtkCellRenderer *cell); -GDK_AVAILABLE_IN_ALL -gboolean gtk_icon_view_get_tooltip_context (GtkIconView *icon_view, - int x, - int y, - gboolean keyboard_tip, - GtkTreeModel **model, - GtkTreePath **path, - GtkTreeIter *iter); -GDK_AVAILABLE_IN_ALL -void gtk_icon_view_set_tooltip_column (GtkIconView *icon_view, - int column); -GDK_AVAILABLE_IN_ALL -int gtk_icon_view_get_tooltip_column (GtkIconView *icon_view); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkIconView, g_object_unref) - -G_END_DECLS - -#endif /* __GTK_ICON_VIEW_H__ */ diff --git a/gtk/gtkiconviewprivate.h b/gtk/gtkiconviewprivate.h deleted file mode 100644 index b5fa1358d1..0000000000 --- a/gtk/gtkiconviewprivate.h +++ /dev/null @@ -1,195 +0,0 @@ -/* gtkiconview.h - * Copyright (C) 2002, 2004 Anders Carlsson - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#include "gtk/gtkiconview.h" -#include "gtk/gtkcssnodeprivate.h" -#include "gtk/gtkeventcontrollermotion.h" -#include "gtk/gtkdragsource.h" -#include "gtk/gtkdroptargetasync.h" -#include "gtk/gtkgestureclick.h" - -#ifndef __GTK_ICON_VIEW_PRIVATE_H__ -#define __GTK_ICON_VIEW_PRIVATE_H__ - -typedef struct _GtkIconViewItem GtkIconViewItem; -struct _GtkIconViewItem -{ - GdkRectangle cell_area; - - int index; - - int row, col; - - guint selected : 1; - guint selected_before_rubberbanding : 1; - -}; - -typedef struct _GtkIconViewClass GtkIconViewClass; -typedef struct _GtkIconViewPrivate GtkIconViewPrivate; - -struct _GtkIconView -{ - GtkWidget parent; - - GtkIconViewPrivate *priv; -}; - -struct _GtkIconViewClass -{ - GtkWidgetClass parent_class; - - void (* item_activated) (GtkIconView *icon_view, - GtkTreePath *path); - void (* selection_changed) (GtkIconView *icon_view); - - void (* select_all) (GtkIconView *icon_view); - void (* unselect_all) (GtkIconView *icon_view); - void (* select_cursor_item) (GtkIconView *icon_view); - void (* toggle_cursor_item) (GtkIconView *icon_view); - gboolean (* move_cursor) (GtkIconView *icon_view, - GtkMovementStep step, - int count, - gboolean extend, - gboolean modify); - gboolean (* activate_cursor_item) (GtkIconView *icon_view); -}; - -struct _GtkIconViewPrivate -{ - GtkCellArea *cell_area; - GtkCellAreaContext *cell_area_context; - - gulong add_editable_id; - gulong remove_editable_id; - gulong context_changed_id; - - GPtrArray *row_contexts; - - int width, height; - double mouse_x; - double mouse_y; - - GtkSelectionMode selection_mode; - - GList *children; - - GtkTreeModel *model; - - GList *items; - - GtkEventController *key_controller; - - GtkAdjustment *hadjustment; - GtkAdjustment *vadjustment; - - int rubberband_x1, rubberband_y1; - int rubberband_x2, rubberband_y2; - GdkDevice *rubberband_device; - GtkCssNode *rubberband_node; - - guint scroll_timeout_id; - int scroll_value_diff; - int event_last_x, event_last_y; - - GtkIconViewItem *anchor_item; - GtkIconViewItem *cursor_item; - - GtkIconViewItem *last_single_clicked; - GtkIconViewItem *last_prelight; - - GtkOrientation item_orientation; - - int columns; - int item_width; - int spacing; - int row_spacing; - int column_spacing; - int margin; - int item_padding; - - int text_column; - int markup_column; - int pixbuf_column; - int tooltip_column; - - GtkCellRenderer *pixbuf_cell; - GtkCellRenderer *text_cell; - - /* Drag-and-drop. */ - GdkModifierType start_button_mask; - int pressed_button; - double press_start_x; - double press_start_y; - - GdkContentFormats *source_formats; - GtkDropTargetAsync *dest; - GtkCssNode *dndnode; - - GdkDrag *drag; - - GdkDragAction source_actions; - GdkDragAction dest_actions; - - GtkTreeRowReference *source_item; - GtkTreeRowReference *dest_item; - GtkIconViewDropPosition dest_pos; - - /* scroll to */ - GtkTreeRowReference *scroll_to_path; - float scroll_to_row_align; - float scroll_to_col_align; - guint scroll_to_use_align : 1; - - guint source_set : 1; - guint dest_set : 1; - guint reorderable : 1; - guint empty_view_drop :1; - guint activate_on_single_click : 1; - - guint modify_selection_pressed : 1; - guint extend_selection_pressed : 1; - - guint draw_focus : 1; - - /* GtkScrollablePolicy needs to be checked when - * driving the scrollable adjustment values */ - guint hscroll_policy : 1; - guint vscroll_policy : 1; - - guint doing_rubberband : 1; - -}; - -void _gtk_icon_view_set_cell_data (GtkIconView *icon_view, - GtkIconViewItem *item); -void _gtk_icon_view_set_cursor_item (GtkIconView *icon_view, - GtkIconViewItem *item, - GtkCellRenderer *cursor_cell); -GtkIconViewItem * _gtk_icon_view_get_item_at_coords (GtkIconView *icon_view, - int x, - int y, - gboolean only_in_cell, - GtkCellRenderer **cell_at_pos); -void _gtk_icon_view_select_item (GtkIconView *icon_view, - GtkIconViewItem *item); -void _gtk_icon_view_unselect_item (GtkIconView *icon_view, - GtkIconViewItem *item); - -G_END_DECLS - -#endif /* __GTK_ICON_VIEW_PRIVATE_H__ */ diff --git a/gtk/gtkliststore.c b/gtk/gtkliststore.c deleted file mode 100644 index 4e442bed31..0000000000 --- a/gtk/gtkliststore.c +++ /dev/null @@ -1,2671 +0,0 @@ -/* gtkliststore.c - * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#include "config.h" -#include -#include -#include -#include -#include "gtktreemodel.h" -#include "gtkliststore.h" -#include "gtktreedatalistprivate.h" -#include "gtktreednd.h" -#include "gtkbuildable.h" -#include "gtkbuilderprivate.h" - - -/** - * GtkListStore: - * - * A list-like data structure that can be used with the [class@Gtk.TreeView]. - * - * The `GtkListStore` object is a list model for use with a `GtkTreeView` - * widget. It implements the `GtkTreeModel` interface, and consequentialy, - * can use all of the methods available there. It also implements the - * `GtkTreeSortable` interface so it can be sorted by the view. - * Finally, it also implements the tree - * [drag](iface.TreeDragSource.html) and [drop](iface.TreeDragDest.html) - * interfaces. - * - * The `GtkListStore` can accept most `GType`s as a column type, though - * it can’t accept all custom types. Internally, it will keep a copy of - * data passed in (such as a string or a boxed pointer). Columns that - * accept `GObject`s are handled a little differently. The - * `GtkListStore` will keep a reference to the object instead of copying the - * value. As a result, if the object is modified, it is up to the - * application writer to call [method@Gtk.TreeModel.row_changed] to emit the - * [signal@Gtk.TreeModel::row_changed] signal. This most commonly affects lists - * with [class@Gdk.Texture]s stored. - * - * An example for creating a simple list store: - * - * ```c - * enum { - * COLUMN_STRING, - * COLUMN_INT, - * COLUMN_BOOLEAN, - * N_COLUMNS - * }; - * - * { - * GtkListStore *list_store; - * GtkTreePath *path; - * GtkTreeIter iter; - * int i; - * - * list_store = gtk_list_store_new (N_COLUMNS, - * G_TYPE_STRING, - * G_TYPE_INT, - * G_TYPE_BOOLEAN); - * - * for (i = 0; i < 10; i++) - * { - * char *some_data; - * - * some_data = get_some_data (i); - * - * // Add a new row to the model - * gtk_list_store_append (list_store, &iter); - * gtk_list_store_set (list_store, &iter, - * COLUMN_STRING, some_data, - * COLUMN_INT, i, - * COLUMN_BOOLEAN, FALSE, - * -1); - * - * // As the store will keep a copy of the string internally, - * // we free some_data. - * g_free (some_data); - * } - * - * // Modify a particular row - * path = gtk_tree_path_new_from_string ("4"); - * gtk_tree_model_get_iter (GTK_TREE_MODEL (list_store), - * &iter, - * path); - * gtk_tree_path_free (path); - * gtk_list_store_set (list_store, &iter, - * COLUMN_BOOLEAN, TRUE, - * -1); - * } - * ``` - * - * # Performance Considerations - * - * Internally, the `GtkListStore` was originally implemented with a linked list - * with a tail pointer. As a result, it was fast at data insertion and deletion, - * and not fast at random data access. The `GtkListStore` sets the - * `GTK_TREE_MODEL_ITERS_PERSIST` flag, which means that `GtkTreeIter`s can be - * cached while the row exists. Thus, if access to a particular row is needed - * often and your code is expected to run on older versions of GTK, it is worth - * keeping the iter around. - * - * # Atomic Operations - * - * It is important to note that only the methods - * gtk_list_store_insert_with_values() and gtk_list_store_insert_with_valuesv() - * are atomic, in the sense that the row is being appended to the store and the - * values filled in in a single operation with regard to `GtkTreeModel` signaling. - * In contrast, using e.g. gtk_list_store_append() and then gtk_list_store_set() - * will first create a row, which triggers the `GtkTreeModel::row-inserted` signal - * on `GtkListStore`. The row, however, is still empty, and any signal handler - * connecting to `GtkTreeModel::row-inserted` on this particular store should be prepared - * for the situation that the row might be empty. This is especially important - * if you are wrapping the `GtkListStore` inside a `GtkTreeModel`Filter and are - * using a `GtkTreeModel`FilterVisibleFunc. Using any of the non-atomic operations - * to append rows to the `GtkListStore` will cause the - * `GtkTreeModel`FilterVisibleFunc to be visited with an empty row first; the - * function must be prepared for that. - * - * # GtkListStore as GtkBuildable - * - * The GtkListStore implementation of the [iface@Gtk.Buildable] interface allows - * to specify the model columns with a `` element that may contain - * multiple `` elements, each specifying one model column. The “type” - * attribute specifies the data type for the column. - * - * Additionally, it is possible to specify content for the list store - * in the UI definition, with the `` element. It can contain multiple - * `` elements, each specifying to content for one row of the list model. - * Inside a ``, the `` elements specify the content for individual cells. - * - * Note that it is probably more common to define your models in the code, - * and one might consider it a layering violation to specify the content of - * a list store in a UI definition, data, not presentation, and common wisdom - * is to separate the two, as far as possible. - * - * An example of a UI Definition fragment for a list store: - * - * ```xml - * - * - * - * - * - * - * - * - * John - * Doe - * 25 - * - * - * Johan - * Dahlin - * 50 - * - * - * - * ``` - */ - - -struct _GtkListStorePrivate -{ - GtkTreeIterCompareFunc default_sort_func; - - GDestroyNotify default_sort_destroy; - GList *sort_list; - GType *column_headers; - - int stamp; - int n_columns; - int sort_column_id; - int length; - - GtkSortType order; - - guint columns_dirty : 1; - - gpointer default_sort_data; - gpointer seq; /* head of the list */ -}; - -#define GTK_LIST_STORE_IS_SORTED(list) (((GtkListStore*)(list))->priv->sort_column_id != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID) -static void gtk_list_store_tree_model_init (GtkTreeModelIface *iface); -static void gtk_list_store_drag_source_init(GtkTreeDragSourceIface *iface); -static void gtk_list_store_drag_dest_init (GtkTreeDragDestIface *iface); -static void gtk_list_store_sortable_init (GtkTreeSortableIface *iface); -static void gtk_list_store_buildable_init (GtkBuildableIface *iface); -static void gtk_list_store_finalize (GObject *object); -static GtkTreeModelFlags gtk_list_store_get_flags (GtkTreeModel *tree_model); -static int gtk_list_store_get_n_columns (GtkTreeModel *tree_model); -static GType gtk_list_store_get_column_type (GtkTreeModel *tree_model, - int index); -static gboolean gtk_list_store_get_iter (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreePath *path); -static GtkTreePath *gtk_list_store_get_path (GtkTreeModel *tree_model, - GtkTreeIter *iter); -static void gtk_list_store_get_value (GtkTreeModel *tree_model, - GtkTreeIter *iter, - int column, - GValue *value); -static gboolean gtk_list_store_iter_next (GtkTreeModel *tree_model, - GtkTreeIter *iter); -static gboolean gtk_list_store_iter_previous (GtkTreeModel *tree_model, - GtkTreeIter *iter); -static gboolean gtk_list_store_iter_children (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent); -static gboolean gtk_list_store_iter_has_child (GtkTreeModel *tree_model, - GtkTreeIter *iter); -static int gtk_list_store_iter_n_children (GtkTreeModel *tree_model, - GtkTreeIter *iter); -static gboolean gtk_list_store_iter_nth_child (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent, - int n); -static gboolean gtk_list_store_iter_parent (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *child); - - -static void gtk_list_store_set_n_columns (GtkListStore *list_store, - int n_columns); -static void gtk_list_store_set_column_type (GtkListStore *list_store, - int column, - GType type); - -static void gtk_list_store_increment_stamp (GtkListStore *list_store); - - -/* Drag and Drop */ -static gboolean real_gtk_list_store_row_draggable (GtkTreeDragSource *drag_source, - GtkTreePath *path); -static gboolean gtk_list_store_drag_data_delete (GtkTreeDragSource *drag_source, - GtkTreePath *path); -static GdkContentProvider * - gtk_list_store_drag_data_get (GtkTreeDragSource *drag_source, - GtkTreePath *path); -static gboolean gtk_list_store_drag_data_received (GtkTreeDragDest *drag_dest, - GtkTreePath *dest, - const GValue *value); -static gboolean gtk_list_store_row_drop_possible (GtkTreeDragDest *drag_dest, - GtkTreePath *dest_path, - const GValue *value); - - -/* sortable */ -static void gtk_list_store_sort (GtkListStore *list_store); -static void gtk_list_store_sort_iter_changed (GtkListStore *list_store, - GtkTreeIter *iter, - int column); -static gboolean gtk_list_store_get_sort_column_id (GtkTreeSortable *sortable, - int *sort_column_id, - GtkSortType *order); -static void gtk_list_store_set_sort_column_id (GtkTreeSortable *sortable, - int sort_column_id, - GtkSortType order); -static void gtk_list_store_set_sort_func (GtkTreeSortable *sortable, - int sort_column_id, - GtkTreeIterCompareFunc func, - gpointer data, - GDestroyNotify destroy); -static void gtk_list_store_set_default_sort_func (GtkTreeSortable *sortable, - GtkTreeIterCompareFunc func, - gpointer data, - GDestroyNotify destroy); -static gboolean gtk_list_store_has_default_sort_func (GtkTreeSortable *sortable); - - -/* buildable */ -static gboolean gtk_list_store_buildable_custom_tag_start (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const char *tagname, - GtkBuildableParser *parser, - gpointer *data); -static void gtk_list_store_buildable_custom_tag_end (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const char *tagname, - gpointer data); - -G_DEFINE_TYPE_WITH_CODE (GtkListStore, gtk_list_store, G_TYPE_OBJECT, - G_ADD_PRIVATE (GtkListStore) - G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, - gtk_list_store_tree_model_init) - G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE, - gtk_list_store_drag_source_init) - G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_DEST, - gtk_list_store_drag_dest_init) - G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_SORTABLE, - gtk_list_store_sortable_init) - G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, - gtk_list_store_buildable_init)) - - -static void -gtk_list_store_class_init (GtkListStoreClass *class) -{ - GObjectClass *object_class; - - object_class = (GObjectClass*) class; - - object_class->finalize = gtk_list_store_finalize; -} - -static void -gtk_list_store_tree_model_init (GtkTreeModelIface *iface) -{ - iface->get_flags = gtk_list_store_get_flags; - iface->get_n_columns = gtk_list_store_get_n_columns; - iface->get_column_type = gtk_list_store_get_column_type; - iface->get_iter = gtk_list_store_get_iter; - iface->get_path = gtk_list_store_get_path; - iface->get_value = gtk_list_store_get_value; - iface->iter_next = gtk_list_store_iter_next; - iface->iter_previous = gtk_list_store_iter_previous; - iface->iter_children = gtk_list_store_iter_children; - iface->iter_has_child = gtk_list_store_iter_has_child; - iface->iter_n_children = gtk_list_store_iter_n_children; - iface->iter_nth_child = gtk_list_store_iter_nth_child; - iface->iter_parent = gtk_list_store_iter_parent; -} - -static void -gtk_list_store_drag_source_init (GtkTreeDragSourceIface *iface) -{ - iface->row_draggable = real_gtk_list_store_row_draggable; - iface->drag_data_delete = gtk_list_store_drag_data_delete; - iface->drag_data_get = gtk_list_store_drag_data_get; -} - -static void -gtk_list_store_drag_dest_init (GtkTreeDragDestIface *iface) -{ - iface->drag_data_received = gtk_list_store_drag_data_received; - iface->row_drop_possible = gtk_list_store_row_drop_possible; -} - -static void -gtk_list_store_sortable_init (GtkTreeSortableIface *iface) -{ - iface->get_sort_column_id = gtk_list_store_get_sort_column_id; - iface->set_sort_column_id = gtk_list_store_set_sort_column_id; - iface->set_sort_func = gtk_list_store_set_sort_func; - iface->set_default_sort_func = gtk_list_store_set_default_sort_func; - iface->has_default_sort_func = gtk_list_store_has_default_sort_func; -} - -void -gtk_list_store_buildable_init (GtkBuildableIface *iface) -{ - iface->custom_tag_start = gtk_list_store_buildable_custom_tag_start; - iface->custom_tag_end = gtk_list_store_buildable_custom_tag_end; -} - -static void -gtk_list_store_init (GtkListStore *list_store) -{ - GtkListStorePrivate *priv; - - list_store->priv = gtk_list_store_get_instance_private (list_store); - priv = list_store->priv; - - priv->seq = g_sequence_new (NULL); - priv->sort_list = NULL; - priv->stamp = g_random_int (); - priv->sort_column_id = GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID; - priv->columns_dirty = FALSE; - priv->length = 0; -} - -static gboolean -iter_is_valid (GtkTreeIter *iter, - GtkListStore *list_store) -{ - return iter != NULL && - iter->user_data != NULL && - list_store->priv->stamp == iter->stamp && - !g_sequence_iter_is_end (iter->user_data) && - g_sequence_iter_get_sequence (iter->user_data) == list_store->priv->seq; -} - -/** - * gtk_list_store_new: - * @n_columns: number of columns in the list store - * @...: all `GType` types for the columns, from first to last - * - * Creates a new list store as with @n_columns columns each of the types passed - * in. Note that only types derived from standard GObject fundamental types - * are supported. - * - * As an example, `gtk_list_store_new (3, G_TYPE_INT, G_TYPE_STRING, - * GDK_TYPE_TEXTURE);` will create a new `GtkListStore` with three columns, of type - * int, string and `GdkTexture`, respectively. - * - * Returns: a new `GtkListStore` - */ -GtkListStore * -gtk_list_store_new (int n_columns, - ...) -{ - GtkListStore *retval; - va_list args; - int i; - - g_return_val_if_fail (n_columns > 0, NULL); - - retval = g_object_new (GTK_TYPE_LIST_STORE, NULL); - gtk_list_store_set_n_columns (retval, n_columns); - - va_start (args, n_columns); - - for (i = 0; i < n_columns; i++) - { - GType type = va_arg (args, GType); - if (! _gtk_tree_data_list_check_type (type)) - { - g_warning ("%s: Invalid type %s", G_STRLOC, g_type_name (type)); - g_object_unref (retval); - va_end (args); - - return NULL; - } - - gtk_list_store_set_column_type (retval, i, type); - } - - va_end (args); - - return retval; -} - - -/** - * gtk_list_store_newv: (rename-to gtk_list_store_new) - * @n_columns: number of columns in the list store - * @types: (array length=n_columns): an array of `GType` types for the columns, from first to last - * - * Non-vararg creation function. Used primarily by language bindings. - * - * Returns: (transfer full): a new `GtkListStore` - **/ -GtkListStore * -gtk_list_store_newv (int n_columns, - GType *types) -{ - GtkListStore *retval; - int i; - - g_return_val_if_fail (n_columns > 0, NULL); - - retval = g_object_new (GTK_TYPE_LIST_STORE, NULL); - gtk_list_store_set_n_columns (retval, n_columns); - - for (i = 0; i < n_columns; i++) - { - if (! _gtk_tree_data_list_check_type (types[i])) - { - g_warning ("%s: Invalid type %s", G_STRLOC, g_type_name (types[i])); - g_object_unref (retval); - return NULL; - } - - gtk_list_store_set_column_type (retval, i, types[i]); - } - - return retval; -} - -/** - * gtk_list_store_set_column_types: - * @list_store: A `GtkListStore` - * @n_columns: Number of columns for the list store - * @types: (array length=n_columns): An array length n of `GType`s - * - * This function is meant primarily for `GObject`s that inherit from `GtkListStore`, - * and should only be used when constructing a new `GtkListStore`. It will not - * function after a row has been added, or a method on the `GtkTreeModel` - * interface is called. - **/ -void -gtk_list_store_set_column_types (GtkListStore *list_store, - int n_columns, - GType *types) -{ - GtkListStorePrivate *priv; - int i; - - g_return_if_fail (GTK_IS_LIST_STORE (list_store)); - - priv = list_store->priv; - - g_return_if_fail (priv->columns_dirty == 0); - - gtk_list_store_set_n_columns (list_store, n_columns); - for (i = 0; i < n_columns; i++) - { - if (! _gtk_tree_data_list_check_type (types[i])) - { - g_warning ("%s: Invalid type %s", G_STRLOC, g_type_name (types[i])); - continue; - } - gtk_list_store_set_column_type (list_store, i, types[i]); - } -} - -static void -gtk_list_store_set_n_columns (GtkListStore *list_store, - int n_columns) -{ - GtkListStorePrivate *priv = list_store->priv; - int i; - - if (priv->n_columns == n_columns) - return; - - priv->column_headers = g_renew (GType, priv->column_headers, n_columns); - for (i = priv->n_columns; i < n_columns; i++) - priv->column_headers[i] = G_TYPE_INVALID; - priv->n_columns = n_columns; - - if (priv->sort_list) - _gtk_tree_data_list_header_free (priv->sort_list); - priv->sort_list = _gtk_tree_data_list_header_new (n_columns, priv->column_headers); -} - -static void -gtk_list_store_set_column_type (GtkListStore *list_store, - int column, - GType type) -{ - GtkListStorePrivate *priv = list_store->priv; - - if (!_gtk_tree_data_list_check_type (type)) - { - g_warning ("%s: Invalid type %s", G_STRLOC, g_type_name (type)); - return; - } - - priv->column_headers[column] = type; -} - -static void -gtk_list_store_finalize (GObject *object) -{ - GtkListStore *list_store = GTK_LIST_STORE (object); - GtkListStorePrivate *priv = list_store->priv; - - g_sequence_foreach (priv->seq, - (GFunc) _gtk_tree_data_list_free, priv->column_headers); - - g_sequence_free (priv->seq); - - _gtk_tree_data_list_header_free (priv->sort_list); - g_free (priv->column_headers); - - if (priv->default_sort_destroy) - { - GDestroyNotify d = priv->default_sort_destroy; - - priv->default_sort_destroy = NULL; - d (priv->default_sort_data); - priv->default_sort_data = NULL; - } - - G_OBJECT_CLASS (gtk_list_store_parent_class)->finalize (object); -} - -/* Fulfill the GtkTreeModel requirements */ -static GtkTreeModelFlags -gtk_list_store_get_flags (GtkTreeModel *tree_model) -{ - return GTK_TREE_MODEL_ITERS_PERSIST | GTK_TREE_MODEL_LIST_ONLY; -} - -static int -gtk_list_store_get_n_columns (GtkTreeModel *tree_model) -{ - GtkListStore *list_store = GTK_LIST_STORE (tree_model); - GtkListStorePrivate *priv = list_store->priv; - - priv->columns_dirty = TRUE; - - return priv->n_columns; -} - -static GType -gtk_list_store_get_column_type (GtkTreeModel *tree_model, - int index) -{ - GtkListStore *list_store = GTK_LIST_STORE (tree_model); - GtkListStorePrivate *priv = list_store->priv; - - g_return_val_if_fail (index < priv->n_columns, G_TYPE_INVALID); - - priv->columns_dirty = TRUE; - - return priv->column_headers[index]; -} - -static gboolean -gtk_list_store_get_iter (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreePath *path) -{ - GtkListStore *list_store = GTK_LIST_STORE (tree_model); - GtkListStorePrivate *priv = list_store->priv; - GSequence *seq; - int i; - - priv->columns_dirty = TRUE; - - seq = priv->seq; - - i = gtk_tree_path_get_indices (path)[0]; - - if (i >= g_sequence_get_length (seq)) - { - iter->stamp = 0; - return FALSE; - } - - iter->stamp = priv->stamp; - iter->user_data = g_sequence_get_iter_at_pos (seq, i); - - return TRUE; -} - -static GtkTreePath * -gtk_list_store_get_path (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - GtkListStore *list_store = GTK_LIST_STORE (tree_model); - GtkListStorePrivate *priv = list_store->priv; - GtkTreePath *path; - - g_return_val_if_fail (iter->stamp == priv->stamp, NULL); - - if (g_sequence_iter_is_end (iter->user_data)) - return NULL; - - path = gtk_tree_path_new (); - gtk_tree_path_append_index (path, g_sequence_iter_get_position (iter->user_data)); - - return path; -} - -static void -gtk_list_store_get_value (GtkTreeModel *tree_model, - GtkTreeIter *iter, - int column, - GValue *value) -{ - GtkListStore *list_store = GTK_LIST_STORE (tree_model); - GtkListStorePrivate *priv = list_store->priv; - GtkTreeDataList *list; - int tmp_column = column; - - g_return_if_fail (column < priv->n_columns); - g_return_if_fail (iter_is_valid (iter, list_store)); - - list = g_sequence_get (iter->user_data); - - while (tmp_column-- > 0 && list) - list = list->next; - - if (list == NULL) - g_value_init (value, priv->column_headers[column]); - else - _gtk_tree_data_list_node_to_value (list, - priv->column_headers[column], - value); -} - -static gboolean -gtk_list_store_iter_next (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - GtkListStore *list_store = GTK_LIST_STORE (tree_model); - GtkListStorePrivate *priv = list_store->priv; - gboolean retval; - - g_return_val_if_fail (priv->stamp == iter->stamp, FALSE); - iter->user_data = g_sequence_iter_next (iter->user_data); - - retval = g_sequence_iter_is_end (iter->user_data); - if (retval) - iter->stamp = 0; - - return !retval; -} - -static gboolean -gtk_list_store_iter_previous (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - GtkListStore *list_store = GTK_LIST_STORE (tree_model); - GtkListStorePrivate *priv = list_store->priv; - - g_return_val_if_fail (priv->stamp == iter->stamp, FALSE); - - if (g_sequence_iter_is_begin (iter->user_data)) - { - iter->stamp = 0; - return FALSE; - } - - iter->user_data = g_sequence_iter_prev (iter->user_data); - - return TRUE; -} - -static gboolean -gtk_list_store_iter_children (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent) -{ - GtkListStore *list_store = (GtkListStore *) tree_model; - GtkListStorePrivate *priv = list_store->priv; - - /* this is a list, nodes have no children */ - if (parent) - { - iter->stamp = 0; - return FALSE; - } - - if (g_sequence_get_length (priv->seq) > 0) - { - iter->stamp = priv->stamp; - iter->user_data = g_sequence_get_begin_iter (priv->seq); - return TRUE; - } - else - { - iter->stamp = 0; - return FALSE; - } -} - -static gboolean -gtk_list_store_iter_has_child (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - return FALSE; -} - -static int -gtk_list_store_iter_n_children (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - GtkListStore *list_store = GTK_LIST_STORE (tree_model); - GtkListStorePrivate *priv = list_store->priv; - - if (iter == NULL) - return g_sequence_get_length (priv->seq); - - g_return_val_if_fail (priv->stamp == iter->stamp, -1); - - return 0; -} - -static gboolean -gtk_list_store_iter_nth_child (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent, - int n) -{ - GtkListStore *list_store = GTK_LIST_STORE (tree_model); - GtkListStorePrivate *priv = list_store->priv; - GSequenceIter *child; - - iter->stamp = 0; - - if (parent) - return FALSE; - - child = g_sequence_get_iter_at_pos (priv->seq, n); - - if (g_sequence_iter_is_end (child)) - return FALSE; - - iter->stamp = priv->stamp; - iter->user_data = child; - - return TRUE; -} - -static gboolean -gtk_list_store_iter_parent (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *child) -{ - iter->stamp = 0; - return FALSE; -} - -static gboolean -gtk_list_store_real_set_value (GtkListStore *list_store, - GtkTreeIter *iter, - int column, - GValue *value, - gboolean sort) -{ - GtkListStorePrivate *priv = list_store->priv; - GtkTreeDataList *list; - GtkTreeDataList *prev; - int old_column = column; - GValue real_value = G_VALUE_INIT; - gboolean converted = FALSE; - gboolean retval = FALSE; - - if (! g_type_is_a (G_VALUE_TYPE (value), priv->column_headers[column])) - { - if (! (g_value_type_transformable (G_VALUE_TYPE (value), priv->column_headers[column]))) - { - g_warning ("%s: Unable to convert from %s to %s", - G_STRLOC, - g_type_name (G_VALUE_TYPE (value)), - g_type_name (priv->column_headers[column])); - return retval; - } - - g_value_init (&real_value, priv->column_headers[column]); - if (!g_value_transform (value, &real_value)) - { - g_warning ("%s: Unable to make conversion from %s to %s", - G_STRLOC, - g_type_name (G_VALUE_TYPE (value)), - g_type_name (priv->column_headers[column])); - g_value_unset (&real_value); - return retval; - } - converted = TRUE; - } - - prev = list = g_sequence_get (iter->user_data); - - while (list != NULL) - { - if (column == 0) - { - if (converted) - _gtk_tree_data_list_value_to_node (list, &real_value); - else - _gtk_tree_data_list_value_to_node (list, value); - retval = TRUE; - if (converted) - g_value_unset (&real_value); - if (sort && GTK_LIST_STORE_IS_SORTED (list_store)) - gtk_list_store_sort_iter_changed (list_store, iter, old_column); - return retval; - } - - column--; - prev = list; - list = list->next; - } - - if (g_sequence_get (iter->user_data) == NULL) - { - list = _gtk_tree_data_list_alloc(); - g_sequence_set (iter->user_data, list); - list->next = NULL; - } - else - { - list = prev->next = _gtk_tree_data_list_alloc (); - list->next = NULL; - } - - while (column != 0) - { - list->next = _gtk_tree_data_list_alloc (); - list = list->next; - list->next = NULL; - column --; - } - - if (converted) - _gtk_tree_data_list_value_to_node (list, &real_value); - else - _gtk_tree_data_list_value_to_node (list, value); - - retval = TRUE; - if (converted) - g_value_unset (&real_value); - - if (sort && GTK_LIST_STORE_IS_SORTED (list_store)) - gtk_list_store_sort_iter_changed (list_store, iter, old_column); - - return retval; -} - - -/** - * gtk_list_store_set_value: - * @list_store: A `GtkListStore` - * @iter: A valid `GtkTreeIter` for the row being modified - * @column: column number to modify - * @value: new value for the cell - * - * Sets the data in the cell specified by @iter and @column. - * The type of @value must be convertible to the type of the - * column. - * - **/ -void -gtk_list_store_set_value (GtkListStore *list_store, - GtkTreeIter *iter, - int column, - GValue *value) -{ - GtkListStorePrivate *priv; - - g_return_if_fail (GTK_IS_LIST_STORE (list_store)); - g_return_if_fail (iter_is_valid (iter, list_store)); - g_return_if_fail (G_IS_VALUE (value)); - priv = list_store->priv; - g_return_if_fail (column >= 0 && column < priv->n_columns); - - if (gtk_list_store_real_set_value (list_store, iter, column, value, TRUE)) - { - GtkTreePath *path; - - path = gtk_list_store_get_path (GTK_TREE_MODEL (list_store), iter); - gtk_tree_model_row_changed (GTK_TREE_MODEL (list_store), path, iter); - gtk_tree_path_free (path); - } -} - -static GtkTreeIterCompareFunc -gtk_list_store_get_compare_func (GtkListStore *list_store) -{ - GtkListStorePrivate *priv = list_store->priv; - GtkTreeIterCompareFunc func = NULL; - - if (GTK_LIST_STORE_IS_SORTED (list_store)) - { - if (priv->sort_column_id != -1) - { - GtkTreeDataSortHeader *header; - header = _gtk_tree_data_list_get_header (priv->sort_list, - priv->sort_column_id); - g_return_val_if_fail (header != NULL, NULL); - g_return_val_if_fail (header->func != NULL, NULL); - func = header->func; - } - else - { - func = priv->default_sort_func; - } - } - - return func; -} - -static void -gtk_list_store_set_vector_internal (GtkListStore *list_store, - GtkTreeIter *iter, - gboolean *emit_signal, - gboolean *maybe_need_sort, - int *columns, - GValue *values, - int n_values) -{ - GtkListStorePrivate *priv = list_store->priv; - int i; - GtkTreeIterCompareFunc func = NULL; - - func = gtk_list_store_get_compare_func (list_store); - if (func != _gtk_tree_data_list_compare_func) - *maybe_need_sort = TRUE; - - for (i = 0; i < n_values; i++) - { - *emit_signal = gtk_list_store_real_set_value (list_store, - iter, - columns[i], - &values[i], - FALSE) || *emit_signal; - - if (func == _gtk_tree_data_list_compare_func && - columns[i] == priv->sort_column_id) - *maybe_need_sort = TRUE; - } -} - -static void -gtk_list_store_set_valist_internal (GtkListStore *list_store, - GtkTreeIter *iter, - gboolean *emit_signal, - gboolean *maybe_need_sort, - va_list var_args) -{ - GtkListStorePrivate *priv = list_store->priv; - int column; - GtkTreeIterCompareFunc func = NULL; - - column = va_arg (var_args, int); - - func = gtk_list_store_get_compare_func (list_store); - if (func != _gtk_tree_data_list_compare_func) - *maybe_need_sort = TRUE; - - while (column != -1) - { - GValue value = G_VALUE_INIT; - char *error = NULL; - - if (column < 0 || column >= priv->n_columns) - { - g_warning ("%s: Invalid column number %d added to iter (remember to end your list of columns with a -1)", G_STRLOC, column); - break; - } - - G_VALUE_COLLECT_INIT (&value, priv->column_headers[column], - var_args, 0, &error); - if (error) - { - g_warning ("%s: %s", G_STRLOC, error); - g_free (error); - - /* we purposely leak the value here, it might not be - * in a sane state if an error condition occurred - */ - break; - } - - /* FIXME: instead of calling this n times, refactor with above */ - *emit_signal = gtk_list_store_real_set_value (list_store, - iter, - column, - &value, - FALSE) || *emit_signal; - - if (func == _gtk_tree_data_list_compare_func && - column == priv->sort_column_id) - *maybe_need_sort = TRUE; - - g_value_unset (&value); - - column = va_arg (var_args, int); - } -} - -/** - * gtk_list_store_set_valuesv: (rename-to gtk_list_store_set) - * @list_store: A `GtkListStore` - * @iter: A valid `GtkTreeIter` for the row being modified - * @columns: (array length=n_values): an array of column numbers - * @values: (array length=n_values): an array of GValues - * @n_values: the length of the @columns and @values arrays - * - * A variant of gtk_list_store_set_valist() which - * takes the columns and values as two arrays, instead of - * varargs. This function is mainly intended for - * language-bindings and in case the number of columns to - * change is not known until run-time. - */ -void -gtk_list_store_set_valuesv (GtkListStore *list_store, - GtkTreeIter *iter, - int *columns, - GValue *values, - int n_values) -{ - GtkListStorePrivate *priv; - gboolean emit_signal = FALSE; - gboolean maybe_need_sort = FALSE; - - g_return_if_fail (GTK_IS_LIST_STORE (list_store)); - g_return_if_fail (iter_is_valid (iter, list_store)); - - priv = list_store->priv; - - gtk_list_store_set_vector_internal (list_store, iter, - &emit_signal, - &maybe_need_sort, - columns, values, n_values); - - if (maybe_need_sort && GTK_LIST_STORE_IS_SORTED (list_store)) - gtk_list_store_sort_iter_changed (list_store, iter, priv->sort_column_id); - - if (emit_signal) - { - GtkTreePath *path; - - path = gtk_list_store_get_path (GTK_TREE_MODEL (list_store), iter); - gtk_tree_model_row_changed (GTK_TREE_MODEL (list_store), path, iter); - gtk_tree_path_free (path); - } -} - -/** - * gtk_list_store_set_valist: - * @list_store: A `GtkListStore` - * @iter: A valid `GtkTreeIter` for the row being modified - * @var_args: va_list of column/value pairs - * - * See gtk_list_store_set(); this version takes a va_list for use by language - * bindings. - * - **/ -void -gtk_list_store_set_valist (GtkListStore *list_store, - GtkTreeIter *iter, - va_list var_args) -{ - GtkListStorePrivate *priv; - gboolean emit_signal = FALSE; - gboolean maybe_need_sort = FALSE; - - g_return_if_fail (GTK_IS_LIST_STORE (list_store)); - g_return_if_fail (iter_is_valid (iter, list_store)); - - priv = list_store->priv; - - gtk_list_store_set_valist_internal (list_store, iter, - &emit_signal, - &maybe_need_sort, - var_args); - - if (maybe_need_sort && GTK_LIST_STORE_IS_SORTED (list_store)) - gtk_list_store_sort_iter_changed (list_store, iter, priv->sort_column_id); - - if (emit_signal) - { - GtkTreePath *path; - - path = gtk_list_store_get_path (GTK_TREE_MODEL (list_store), iter); - gtk_tree_model_row_changed (GTK_TREE_MODEL (list_store), path, iter); - gtk_tree_path_free (path); - } -} - -/** - * gtk_list_store_set: - * @list_store: a `GtkListStore` - * @iter: row iterator - * @...: pairs of column number and value, terminated with -1 - * - * Sets the value of one or more cells in the row referenced by @iter. - * The variable argument list should contain integer column numbers, - * each column number followed by the value to be set. - * The list is terminated by a -1. For example, to set column 0 with type - * %G_TYPE_STRING to “Foo”, you would write `gtk_list_store_set (store, iter, - * 0, "Foo", -1)`. - * - * The value will be referenced by the store if it is a %G_TYPE_OBJECT, and it - * will be copied if it is a %G_TYPE_STRING or %G_TYPE_BOXED. - */ -void -gtk_list_store_set (GtkListStore *list_store, - GtkTreeIter *iter, - ...) -{ - va_list var_args; - - va_start (var_args, iter); - gtk_list_store_set_valist (list_store, iter, var_args); - va_end (var_args); -} - -/** - * gtk_list_store_remove: - * @list_store: A `GtkListStore` - * @iter: A valid `GtkTreeIter` - * - * Removes the given row from the list store. After being removed, - * @iter is set to be the next valid row, or invalidated if it pointed - * to the last row in @list_store. - * - * Returns: %TRUE if @iter is valid, %FALSE if not. - **/ -gboolean -gtk_list_store_remove (GtkListStore *list_store, - GtkTreeIter *iter) -{ - GtkListStorePrivate *priv; - GtkTreePath *path; - GSequenceIter *ptr, *next; - - g_return_val_if_fail (GTK_IS_LIST_STORE (list_store), FALSE); - g_return_val_if_fail (iter_is_valid (iter, list_store), FALSE); - - priv = list_store->priv; - - path = gtk_list_store_get_path (GTK_TREE_MODEL (list_store), iter); - - ptr = iter->user_data; - next = g_sequence_iter_next (ptr); - - _gtk_tree_data_list_free (g_sequence_get (ptr), priv->column_headers); - g_sequence_remove (iter->user_data); - - priv->length--; - - gtk_tree_model_row_deleted (GTK_TREE_MODEL (list_store), path); - gtk_tree_path_free (path); - - if (g_sequence_iter_is_end (next)) - { - iter->stamp = 0; - return FALSE; - } - else - { - iter->stamp = priv->stamp; - iter->user_data = next; - return TRUE; - } -} - -/** - * gtk_list_store_insert: - * @list_store: A `GtkListStore` - * @iter: (out): An unset `GtkTreeIter` to set to the new row - * @position: position to insert the new row, or -1 for last - * - * Creates a new row at @position. @iter will be changed to point to this new - * row. If @position is -1 or is larger than the number of rows on the list, - * then the new row will be appended to the list. The row will be empty after - * this function is called. To fill in values, you need to call - * gtk_list_store_set() or gtk_list_store_set_value(). - * - **/ -void -gtk_list_store_insert (GtkListStore *list_store, - GtkTreeIter *iter, - int position) -{ - GtkListStorePrivate *priv; - GtkTreePath *path; - GSequence *seq; - GSequenceIter *ptr; - int length; - - g_return_if_fail (GTK_IS_LIST_STORE (list_store)); - g_return_if_fail (iter != NULL); - - priv = list_store->priv; - - priv->columns_dirty = TRUE; - - seq = priv->seq; - - length = g_sequence_get_length (seq); - if (position > length || position < 0) - position = length; - - ptr = g_sequence_get_iter_at_pos (seq, position); - ptr = g_sequence_insert_before (ptr, NULL); - - iter->stamp = priv->stamp; - iter->user_data = ptr; - - g_assert (iter_is_valid (iter, list_store)); - - priv->length++; - - path = gtk_tree_path_new (); - gtk_tree_path_append_index (path, position); - gtk_tree_model_row_inserted (GTK_TREE_MODEL (list_store), path, iter); - gtk_tree_path_free (path); -} - -/** - * gtk_list_store_insert_before: - * @list_store: A `GtkListStore` - * @iter: (out): An unset `GtkTreeIter` to set to the new row - * @sibling: (nullable): A valid `GtkTreeIter` - * - * Inserts a new row before @sibling. If @sibling is %NULL, then the row will - * be appended to the end of the list. @iter will be changed to point to this - * new row. The row will be empty after this function is called. To fill in - * values, you need to call gtk_list_store_set() or gtk_list_store_set_value(). - * - **/ -void -gtk_list_store_insert_before (GtkListStore *list_store, - GtkTreeIter *iter, - GtkTreeIter *sibling) -{ - GtkListStorePrivate *priv; - GSequenceIter *after; - - g_return_if_fail (GTK_IS_LIST_STORE (list_store)); - g_return_if_fail (iter != NULL); - - priv = list_store->priv; - - if (sibling) - g_return_if_fail (iter_is_valid (sibling, list_store)); - - if (!sibling) - after = g_sequence_get_end_iter (priv->seq); - else - after = sibling->user_data; - - gtk_list_store_insert (list_store, iter, g_sequence_iter_get_position (after)); -} - -/** - * gtk_list_store_insert_after: - * @list_store: A `GtkListStore` - * @iter: (out): An unset `GtkTreeIter` to set to the new row - * @sibling: (nullable): A valid `GtkTreeIter` - * - * Inserts a new row after @sibling. If @sibling is %NULL, then the row will be - * prepended to the beginning of the list. @iter will be changed to point to - * this new row. The row will be empty after this function is called. To fill - * in values, you need to call gtk_list_store_set() or gtk_list_store_set_value(). - * - **/ -void -gtk_list_store_insert_after (GtkListStore *list_store, - GtkTreeIter *iter, - GtkTreeIter *sibling) -{ - GtkListStorePrivate *priv; - GSequenceIter *after; - - g_return_if_fail (GTK_IS_LIST_STORE (list_store)); - g_return_if_fail (iter != NULL); - - priv = list_store->priv; - - if (sibling) - g_return_if_fail (iter_is_valid (sibling, list_store)); - - if (!sibling) - after = g_sequence_get_begin_iter (priv->seq); - else - after = g_sequence_iter_next (sibling->user_data); - - gtk_list_store_insert (list_store, iter, g_sequence_iter_get_position (after)); -} - -/** - * gtk_list_store_prepend: - * @list_store: A `GtkListStore` - * @iter: (out): An unset `GtkTreeIter` to set to the prepend row - * - * Prepends a new row to @list_store. @iter will be changed to point to this new - * row. The row will be empty after this function is called. To fill in - * values, you need to call gtk_list_store_set() or gtk_list_store_set_value(). - * - **/ -void -gtk_list_store_prepend (GtkListStore *list_store, - GtkTreeIter *iter) -{ - g_return_if_fail (GTK_IS_LIST_STORE (list_store)); - g_return_if_fail (iter != NULL); - - gtk_list_store_insert (list_store, iter, 0); -} - -/** - * gtk_list_store_append: - * @list_store: A `GtkListStore` - * @iter: (out): An unset `GtkTreeIter` to set to the appended row - * - * Appends a new row to @list_store. @iter will be changed to point to this new - * row. The row will be empty after this function is called. To fill in - * values, you need to call gtk_list_store_set() or gtk_list_store_set_value(). - * - **/ -void -gtk_list_store_append (GtkListStore *list_store, - GtkTreeIter *iter) -{ - g_return_if_fail (GTK_IS_LIST_STORE (list_store)); - g_return_if_fail (iter != NULL); - - gtk_list_store_insert (list_store, iter, -1); -} - -static void -gtk_list_store_increment_stamp (GtkListStore *list_store) -{ - GtkListStorePrivate *priv = list_store->priv; - - do - { - priv->stamp++; - } - while (priv->stamp == 0); -} - -/** - * gtk_list_store_clear: - * @list_store: a `GtkListStore`. - * - * Removes all rows from the list store. - * - **/ -void -gtk_list_store_clear (GtkListStore *list_store) -{ - GtkListStorePrivate *priv; - GtkTreeIter iter; - - g_return_if_fail (GTK_IS_LIST_STORE (list_store)); - - priv = list_store->priv; - - while (g_sequence_get_length (priv->seq) > 0) - { - iter.stamp = priv->stamp; - iter.user_data = g_sequence_get_begin_iter (priv->seq); - gtk_list_store_remove (list_store, &iter); - } - - gtk_list_store_increment_stamp (list_store); -} - -/** - * gtk_list_store_iter_is_valid: - * @list_store: a list store - * @iter: the iterator to check - * - * Checks if the given iter is a valid iter for this `GtkListStore`. - * - * This function is slow. Only use it for debugging and/or testing - * purposes. - * - * Returns: %TRUE if the iter is valid, %FALSE if the iter is invalid. - **/ -gboolean -gtk_list_store_iter_is_valid (GtkListStore *list_store, - GtkTreeIter *iter) -{ - GtkListStorePrivate *priv; - GSequenceIter *seq_iter; - - g_return_val_if_fail (GTK_IS_LIST_STORE (list_store), FALSE); - g_return_val_if_fail (iter != NULL, FALSE); - - /* can't use iter_is_valid() here, because iter might point - * to random memory. - * - * We MUST NOT dereference it. - */ - - priv = list_store->priv; - - if (iter == NULL || - iter->user_data == NULL || - priv->stamp != iter->stamp) - return FALSE; - - for (seq_iter = g_sequence_get_begin_iter (priv->seq); - !g_sequence_iter_is_end (seq_iter); - seq_iter = g_sequence_iter_next (seq_iter)) - { - if (seq_iter == iter->user_data) - return TRUE; - } - - return FALSE; -} - -static gboolean real_gtk_list_store_row_draggable (GtkTreeDragSource *drag_source, - GtkTreePath *path) -{ - return TRUE; -} - -static gboolean -gtk_list_store_drag_data_delete (GtkTreeDragSource *drag_source, - GtkTreePath *path) -{ - GtkTreeIter iter; - - if (gtk_list_store_get_iter (GTK_TREE_MODEL (drag_source), - &iter, - path)) - { - gtk_list_store_remove (GTK_LIST_STORE (drag_source), &iter); - return TRUE; - } - return FALSE; -} - -static GdkContentProvider * -gtk_list_store_drag_data_get (GtkTreeDragSource *drag_source, - GtkTreePath *path) -{ - /* Note that we don't need to handle the GTK_TREE_MODEL_ROW - * target, because the default handler does it for us, but - * we do anyway for the convenience of someone maybe overriding the - * default handler. - */ - return gtk_tree_create_row_drag_content (GTK_TREE_MODEL (drag_source), path); -} - -static gboolean -gtk_list_store_drag_data_received (GtkTreeDragDest *drag_dest, - GtkTreePath *dest, - const GValue *value) -{ - GtkTreeModel *tree_model = GTK_TREE_MODEL (drag_dest); - GtkListStore *list_store = GTK_LIST_STORE (tree_model); - GtkListStorePrivate *priv = list_store->priv; - GtkTreeModel *src_model = NULL; - GtkTreePath *src_path = NULL; - gboolean retval = FALSE; - - if (gtk_tree_get_row_drag_data (value, - &src_model, - &src_path) && - src_model == tree_model) - { - /* Copy the given row to a new position */ - GtkTreeIter src_iter; - GtkTreeIter dest_iter; - GtkTreePath *prev; - - if (!gtk_list_store_get_iter (src_model, - &src_iter, - src_path)) - { - goto out; - } - - /* Get the path to insert _after_ (dest is the path to insert _before_) */ - prev = gtk_tree_path_copy (dest); - - if (!gtk_tree_path_prev (prev)) - { - /* dest was the first spot in the list; which means we are supposed - * to prepend. - */ - gtk_list_store_prepend (list_store, &dest_iter); - - retval = TRUE; - } - else - { - if (gtk_list_store_get_iter (tree_model, &dest_iter, prev)) - { - GtkTreeIter tmp_iter = dest_iter; - - gtk_list_store_insert_after (list_store, &dest_iter, &tmp_iter); - - retval = TRUE; - } - } - - gtk_tree_path_free (prev); - - /* If we succeeded in creating dest_iter, copy data from src - */ - if (retval) - { - GtkTreeDataList *dl = g_sequence_get (src_iter.user_data); - GtkTreeDataList *copy_head = NULL; - GtkTreeDataList *copy_prev = NULL; - GtkTreeDataList *copy_iter = NULL; - GtkTreePath *path; - int col; - - col = 0; - while (dl) - { - copy_iter = _gtk_tree_data_list_node_copy (dl, - priv->column_headers[col]); - - if (copy_head == NULL) - copy_head = copy_iter; - - if (copy_prev) - copy_prev->next = copy_iter; - - copy_prev = copy_iter; - - dl = dl->next; - ++col; - } - - dest_iter.stamp = priv->stamp; - g_sequence_set (dest_iter.user_data, copy_head); - - path = gtk_list_store_get_path (tree_model, &dest_iter); - gtk_tree_model_row_changed (tree_model, path, &dest_iter); - gtk_tree_path_free (path); - } - } - else - { - /* FIXME maybe add some data targets eventually, or handle text - * targets in the simple case. - */ - } - - out: - - if (src_path) - gtk_tree_path_free (src_path); - - return retval; -} - -static gboolean -gtk_list_store_row_drop_possible (GtkTreeDragDest *drag_dest, - GtkTreePath *dest_path, - const GValue *value) -{ - int *indices; - GtkTreeModel *src_model = NULL; - GtkTreePath *src_path = NULL; - gboolean retval = FALSE; - - /* don't accept drops if the list has been sorted */ - if (GTK_LIST_STORE_IS_SORTED (drag_dest)) - return FALSE; - - if (!gtk_tree_get_row_drag_data (value, - &src_model, - &src_path)) - goto out; - - if (src_model != GTK_TREE_MODEL (drag_dest)) - goto out; - - if (gtk_tree_path_get_depth (dest_path) != 1) - goto out; - - /* can drop before any existing node, or before one past any existing. */ - - indices = gtk_tree_path_get_indices (dest_path); - - if (indices[0] <= g_sequence_get_length (GTK_LIST_STORE (drag_dest)->priv->seq)) - retval = TRUE; - - out: - if (src_path) - gtk_tree_path_free (src_path); - - return retval; -} - -/* Sorting and reordering */ - -/* Reordering */ -static int -gtk_list_store_reorder_func (GSequenceIter *a, - GSequenceIter *b, - gpointer user_data) -{ - GHashTable *new_positions = user_data; - int apos = GPOINTER_TO_INT (g_hash_table_lookup (new_positions, a)); - int bpos = GPOINTER_TO_INT (g_hash_table_lookup (new_positions, b)); - - if (apos < bpos) - return -1; - if (apos > bpos) - return 1; - return 0; -} - -/** - * gtk_list_store_reorder: - * @store: A `GtkListStore`. - * @new_order: (array zero-terminated=1): an array of integers mapping the new - * position of each child to its old position before the re-ordering, - * i.e. @new_order`[newpos] = oldpos`. It must have - * exactly as many items as the list store’s length. - * - * Reorders @store to follow the order indicated by @new_order. Note that - * this function only works with unsorted stores. - **/ -void -gtk_list_store_reorder (GtkListStore *store, - int *new_order) -{ - GtkListStorePrivate *priv; - int i; - GtkTreePath *path; - GHashTable *new_positions; - GSequenceIter *ptr; - int *order; - - g_return_if_fail (GTK_IS_LIST_STORE (store)); - g_return_if_fail (!GTK_LIST_STORE_IS_SORTED (store)); - g_return_if_fail (new_order != NULL); - - priv = store->priv; - - order = g_new (int, g_sequence_get_length (priv->seq)); - for (i = 0; i < g_sequence_get_length (priv->seq); i++) - order[new_order[i]] = i; - - new_positions = g_hash_table_new (g_direct_hash, g_direct_equal); - - ptr = g_sequence_get_begin_iter (priv->seq); - i = 0; - while (!g_sequence_iter_is_end (ptr)) - { - g_hash_table_insert (new_positions, ptr, GINT_TO_POINTER (order[i++])); - - ptr = g_sequence_iter_next (ptr); - } - g_free (order); - - g_sequence_sort_iter (priv->seq, gtk_list_store_reorder_func, new_positions); - - g_hash_table_destroy (new_positions); - - /* emit signal */ - path = gtk_tree_path_new (); - gtk_tree_model_rows_reordered (GTK_TREE_MODEL (store), - path, NULL, new_order); - gtk_tree_path_free (path); -} - -static GHashTable * -save_positions (GSequence *seq) -{ - GHashTable *positions = g_hash_table_new (g_direct_hash, g_direct_equal); - GSequenceIter *ptr; - - ptr = g_sequence_get_begin_iter (seq); - while (!g_sequence_iter_is_end (ptr)) - { - g_hash_table_insert (positions, ptr, - GINT_TO_POINTER (g_sequence_iter_get_position (ptr))); - ptr = g_sequence_iter_next (ptr); - } - - return positions; -} - -static int * -generate_order (GSequence *seq, - GHashTable *old_positions) -{ - GSequenceIter *ptr; - int *order = g_new (int, g_sequence_get_length (seq)); - int i; - - i = 0; - ptr = g_sequence_get_begin_iter (seq); - while (!g_sequence_iter_is_end (ptr)) - { - int old_pos = GPOINTER_TO_INT (g_hash_table_lookup (old_positions, ptr)); - order[i++] = old_pos; - ptr = g_sequence_iter_next (ptr); - } - - g_hash_table_destroy (old_positions); - - return order; -} - -/** - * gtk_list_store_swap: - * @store: A `GtkListStore`. - * @a: A `GtkTreeIter` - * @b: Another `GtkTreeIter` - * - * Swaps @a and @b in @store. Note that this function only works with - * unsorted stores. - **/ -void -gtk_list_store_swap (GtkListStore *store, - GtkTreeIter *a, - GtkTreeIter *b) -{ - GtkListStorePrivate *priv; - GHashTable *old_positions; - int *order; - GtkTreePath *path; - - g_return_if_fail (GTK_IS_LIST_STORE (store)); - g_return_if_fail (!GTK_LIST_STORE_IS_SORTED (store)); - g_return_if_fail (iter_is_valid (a, store)); - g_return_if_fail (iter_is_valid (b, store)); - - priv = store->priv; - - if (a->user_data == b->user_data) - return; - - old_positions = save_positions (priv->seq); - - g_sequence_swap (a->user_data, b->user_data); - - order = generate_order (priv->seq, old_positions); - path = gtk_tree_path_new (); - - gtk_tree_model_rows_reordered (GTK_TREE_MODEL (store), - path, NULL, order); - - gtk_tree_path_free (path); - g_free (order); -} - -static void -gtk_list_store_move_to (GtkListStore *store, - GtkTreeIter *iter, - int new_pos) -{ - GtkListStorePrivate *priv = store->priv; - GHashTable *old_positions; - GtkTreePath *path; - int *order; - - old_positions = save_positions (priv->seq); - - g_sequence_move (iter->user_data, g_sequence_get_iter_at_pos (priv->seq, new_pos)); - - order = generate_order (priv->seq, old_positions); - - path = gtk_tree_path_new (); - gtk_tree_model_rows_reordered (GTK_TREE_MODEL (store), - path, NULL, order); - gtk_tree_path_free (path); - g_free (order); -} - -/** - * gtk_list_store_move_before: - * @store: A `GtkListStore`. - * @iter: A `GtkTreeIter` - * @position: (nullable): A `GtkTreeIter` - * - * Moves @iter in @store to the position before @position. Note that this - * function only works with unsorted stores. If @position is %NULL, @iter - * will be moved to the end of the list. - **/ -void -gtk_list_store_move_before (GtkListStore *store, - GtkTreeIter *iter, - GtkTreeIter *position) -{ - int pos; - - g_return_if_fail (GTK_IS_LIST_STORE (store)); - g_return_if_fail (!GTK_LIST_STORE_IS_SORTED (store)); - g_return_if_fail (iter_is_valid (iter, store)); - if (position) - g_return_if_fail (iter_is_valid (position, store)); - - if (position) - pos = g_sequence_iter_get_position (position->user_data); - else - pos = -1; - - gtk_list_store_move_to (store, iter, pos); -} - -/** - * gtk_list_store_move_after: - * @store: A `GtkListStore`. - * @iter: A `GtkTreeIter` - * @position: (nullable): A `GtkTreeIter` - * - * Moves @iter in @store to the position after @position. Note that this - * function only works with unsorted stores. If @position is %NULL, @iter - * will be moved to the start of the list. - **/ -void -gtk_list_store_move_after (GtkListStore *store, - GtkTreeIter *iter, - GtkTreeIter *position) -{ - int pos; - - g_return_if_fail (GTK_IS_LIST_STORE (store)); - g_return_if_fail (!GTK_LIST_STORE_IS_SORTED (store)); - g_return_if_fail (iter_is_valid (iter, store)); - if (position) - g_return_if_fail (iter_is_valid (position, store)); - - if (position) - pos = g_sequence_iter_get_position (position->user_data) + 1; - else - pos = 0; - - gtk_list_store_move_to (store, iter, pos); -} - -/* Sorting */ -static int -gtk_list_store_compare_func (GSequenceIter *a, - GSequenceIter *b, - gpointer user_data) -{ - GtkListStore *list_store = user_data; - GtkListStorePrivate *priv = list_store->priv; - GtkTreeIter iter_a; - GtkTreeIter iter_b; - int retval; - GtkTreeIterCompareFunc func; - gpointer data; - - if (priv->sort_column_id != -1) - { - GtkTreeDataSortHeader *header; - - header = _gtk_tree_data_list_get_header (priv->sort_list, - priv->sort_column_id); - g_return_val_if_fail (header != NULL, 0); - g_return_val_if_fail (header->func != NULL, 0); - - func = header->func; - data = header->data; - } - else - { - g_return_val_if_fail (priv->default_sort_func != NULL, 0); - func = priv->default_sort_func; - data = priv->default_sort_data; - } - - iter_a.stamp = priv->stamp; - iter_a.user_data = (gpointer)a; - iter_b.stamp = priv->stamp; - iter_b.user_data = (gpointer)b; - - g_assert (iter_is_valid (&iter_a, list_store)); - g_assert (iter_is_valid (&iter_b, list_store)); - - retval = (* func) (GTK_TREE_MODEL (list_store), &iter_a, &iter_b, data); - - if (priv->order == GTK_SORT_DESCENDING) - { - if (retval > 0) - retval = -1; - else if (retval < 0) - retval = 1; - } - - return retval; -} - -static void -gtk_list_store_sort (GtkListStore *list_store) -{ - GtkListStorePrivate *priv = list_store->priv; - int *new_order; - GtkTreePath *path; - GHashTable *old_positions; - - if (!GTK_LIST_STORE_IS_SORTED (list_store) || - g_sequence_get_length (priv->seq) <= 1) - return; - - old_positions = save_positions (priv->seq); - - g_sequence_sort_iter (priv->seq, gtk_list_store_compare_func, list_store); - - /* Let the world know about our new order */ - new_order = generate_order (priv->seq, old_positions); - - path = gtk_tree_path_new (); - gtk_tree_model_rows_reordered (GTK_TREE_MODEL (list_store), - path, NULL, new_order); - gtk_tree_path_free (path); - g_free (new_order); -} - -static gboolean -iter_is_sorted (GtkListStore *list_store, - GtkTreeIter *iter) -{ - GSequenceIter *cmp; - - if (!g_sequence_iter_is_begin (iter->user_data)) - { - cmp = g_sequence_iter_prev (iter->user_data); - if (gtk_list_store_compare_func (cmp, iter->user_data, list_store) > 0) - return FALSE; - } - - cmp = g_sequence_iter_next (iter->user_data); - if (!g_sequence_iter_is_end (cmp)) - { - if (gtk_list_store_compare_func (iter->user_data, cmp, list_store) > 0) - return FALSE; - } - - return TRUE; -} - -static void -gtk_list_store_sort_iter_changed (GtkListStore *list_store, - GtkTreeIter *iter, - int column) - -{ - GtkListStorePrivate *priv = list_store->priv; - GtkTreePath *path; - - path = gtk_list_store_get_path (GTK_TREE_MODEL (list_store), iter); - gtk_tree_model_row_changed (GTK_TREE_MODEL (list_store), path, iter); - gtk_tree_path_free (path); - - if (!iter_is_sorted (list_store, iter)) - { - GHashTable *old_positions; - int *order; - - old_positions = save_positions (priv->seq); - g_sequence_sort_changed_iter (iter->user_data, - gtk_list_store_compare_func, - list_store); - order = generate_order (priv->seq, old_positions); - path = gtk_tree_path_new (); - gtk_tree_model_rows_reordered (GTK_TREE_MODEL (list_store), - path, NULL, order); - gtk_tree_path_free (path); - g_free (order); - } -} - -static gboolean -gtk_list_store_get_sort_column_id (GtkTreeSortable *sortable, - int *sort_column_id, - GtkSortType *order) -{ - GtkListStore *list_store = GTK_LIST_STORE (sortable); - GtkListStorePrivate *priv = list_store->priv; - - if (sort_column_id) - * sort_column_id = priv->sort_column_id; - if (order) - * order = priv->order; - - if (priv->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID || - priv->sort_column_id == GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID) - return FALSE; - - return TRUE; -} - -static void -gtk_list_store_set_sort_column_id (GtkTreeSortable *sortable, - int sort_column_id, - GtkSortType order) -{ - GtkListStore *list_store = GTK_LIST_STORE (sortable); - GtkListStorePrivate *priv = list_store->priv; - - if ((priv->sort_column_id == sort_column_id) && - (priv->order == order)) - return; - - if (sort_column_id != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID) - { - if (sort_column_id != GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID) - { - GtkTreeDataSortHeader *header = NULL; - - header = _gtk_tree_data_list_get_header (priv->sort_list, - sort_column_id); - - /* We want to make sure that we have a function */ - g_return_if_fail (header != NULL); - g_return_if_fail (header->func != NULL); - } - else - { - g_return_if_fail (priv->default_sort_func != NULL); - } - } - - - priv->sort_column_id = sort_column_id; - priv->order = order; - - gtk_tree_sortable_sort_column_changed (sortable); - - gtk_list_store_sort (list_store); -} - -static void -gtk_list_store_set_sort_func (GtkTreeSortable *sortable, - int sort_column_id, - GtkTreeIterCompareFunc func, - gpointer data, - GDestroyNotify destroy) -{ - GtkListStore *list_store = GTK_LIST_STORE (sortable); - GtkListStorePrivate *priv = list_store->priv; - - priv->sort_list = _gtk_tree_data_list_set_header (priv->sort_list, - sort_column_id, - func, data, destroy); - - if (priv->sort_column_id == sort_column_id) - gtk_list_store_sort (list_store); -} - -static void -gtk_list_store_set_default_sort_func (GtkTreeSortable *sortable, - GtkTreeIterCompareFunc func, - gpointer data, - GDestroyNotify destroy) -{ - GtkListStore *list_store = GTK_LIST_STORE (sortable); - GtkListStorePrivate *priv = list_store->priv; - - if (priv->default_sort_destroy) - { - GDestroyNotify d = priv->default_sort_destroy; - - priv->default_sort_destroy = NULL; - d (priv->default_sort_data); - } - - priv->default_sort_func = func; - priv->default_sort_data = data; - priv->default_sort_destroy = destroy; - - if (priv->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID) - gtk_list_store_sort (list_store); -} - -static gboolean -gtk_list_store_has_default_sort_func (GtkTreeSortable *sortable) -{ - GtkListStore *list_store = GTK_LIST_STORE (sortable); - GtkListStorePrivate *priv = list_store->priv; - - return (priv->default_sort_func != NULL); -} - - -/** - * gtk_list_store_insert_with_values: - * @list_store: A `GtkListStore` - * @iter: (out) (optional): An unset `GtkTreeIter` to set to the new row - * @position: position to insert the new row, or -1 to append after existing - * rows - * @...: pairs of column number and value, terminated with -1 - * - * Creates a new row at @position. @iter will be changed to point to this new - * row. If @position is -1, or larger than the number of rows in the list, then - * the new row will be appended to the list. The row will be filled with the - * values given to this function. - * - * Calling - * `gtk_list_store_insert_with_values (list_store, iter, position...)` - * has the same effect as calling: - * - * |[ - * static void - * insert_value (GtkListStore *list_store, - * GtkTreeIter *iter, - * int position) - * { - * gtk_list_store_insert (list_store, iter, position); - * gtk_list_store_set (list_store, - * iter - * // ... - * ); - * } - * ]| - * - * with the difference that the former will only emit `GtkTreeModel`::row-inserted - * once, while the latter will emit `GtkTreeModel`::row-inserted, - * `GtkTreeModel`::row-changed and, if the list store is sorted, - * `GtkTreeModel`::rows-reordered for every inserted value. - * - * Since emitting the `GtkTreeModel::rows-reordered` signal repeatedly can - * affect the performance of the program, gtk_list_store_insert_with_values() - * should generally be preferred when inserting rows in a sorted list store. - */ -void -gtk_list_store_insert_with_values (GtkListStore *list_store, - GtkTreeIter *iter, - int position, - ...) -{ - GtkListStorePrivate *priv; - GtkTreePath *path; - GSequence *seq; - GSequenceIter *ptr; - GtkTreeIter tmp_iter; - int length; - gboolean changed = FALSE; - gboolean maybe_need_sort = FALSE; - va_list var_args; - - /* FIXME: refactor to reduce overlap with gtk_list_store_set() */ - g_return_if_fail (GTK_IS_LIST_STORE (list_store)); - - priv = list_store->priv; - - if (!iter) - iter = &tmp_iter; - - priv->columns_dirty = TRUE; - - seq = priv->seq; - - length = g_sequence_get_length (seq); - if (position > length || position < 0) - position = length; - - ptr = g_sequence_get_iter_at_pos (seq, position); - ptr = g_sequence_insert_before (ptr, NULL); - - iter->stamp = priv->stamp; - iter->user_data = ptr; - - g_assert (iter_is_valid (iter, list_store)); - - priv->length++; - - va_start (var_args, position); - gtk_list_store_set_valist_internal (list_store, iter, - &changed, &maybe_need_sort, - var_args); - va_end (var_args); - - /* Don't emit rows_reordered here */ - if (maybe_need_sort && GTK_LIST_STORE_IS_SORTED (list_store)) - g_sequence_sort_changed_iter (iter->user_data, - gtk_list_store_compare_func, - list_store); - - /* Just emit row_inserted */ - path = gtk_list_store_get_path (GTK_TREE_MODEL (list_store), iter); - gtk_tree_model_row_inserted (GTK_TREE_MODEL (list_store), path, iter); - gtk_tree_path_free (path); -} - - -/** - * gtk_list_store_insert_with_valuesv: (rename-to gtk_list_store_insert_with_values) - * @list_store: A `GtkListStore` - * @iter: (out) (optional): An unset `GtkTreeIter` to set to the new row - * @position: position to insert the new row, or -1 for last - * @columns: (array length=n_values): an array of column numbers - * @values: (array length=n_values): an array of GValues - * @n_values: the length of the @columns and @values arrays - * - * A variant of gtk_list_store_insert_with_values() which - * takes the columns and values as two arrays, instead of - * varargs. - * - * This function is mainly intended for language-bindings. - */ -void -gtk_list_store_insert_with_valuesv (GtkListStore *list_store, - GtkTreeIter *iter, - int position, - int *columns, - GValue *values, - int n_values) -{ - GtkListStorePrivate *priv; - GtkTreePath *path; - GSequence *seq; - GSequenceIter *ptr; - GtkTreeIter tmp_iter; - int length; - gboolean changed = FALSE; - gboolean maybe_need_sort = FALSE; - - /* FIXME refactor to reduce overlap with - * gtk_list_store_insert_with_values() - */ - g_return_if_fail (GTK_IS_LIST_STORE (list_store)); - - priv = list_store->priv; - - if (!iter) - iter = &tmp_iter; - - priv->columns_dirty = TRUE; - - seq = priv->seq; - - length = g_sequence_get_length (seq); - if (position > length || position < 0) - position = length; - - ptr = g_sequence_get_iter_at_pos (seq, position); - ptr = g_sequence_insert_before (ptr, NULL); - - iter->stamp = priv->stamp; - iter->user_data = ptr; - - g_assert (iter_is_valid (iter, list_store)); - - priv->length++; - - gtk_list_store_set_vector_internal (list_store, iter, - &changed, &maybe_need_sort, - columns, values, n_values); - - /* Don't emit rows_reordered here */ - if (maybe_need_sort && GTK_LIST_STORE_IS_SORTED (list_store)) - g_sequence_sort_changed_iter (iter->user_data, - gtk_list_store_compare_func, - list_store); - - /* Just emit row_inserted */ - path = gtk_list_store_get_path (GTK_TREE_MODEL (list_store), iter); - gtk_tree_model_row_inserted (GTK_TREE_MODEL (list_store), path, iter); - gtk_tree_path_free (path); -} - -/* GtkBuildable custom tag implementation - * - * - * - * - * - */ -typedef struct { - gboolean translatable; - char *context; - int id; -} ColInfo; - -typedef struct { - GtkBuilder *builder; - GObject *object; - GSList *column_type_names; - GType *column_types; - GValue *values; - int *colids; - ColInfo **columns; - int last_row; - int n_columns; - int row_column; - gboolean is_data; - const char *domain; -} SubParserData; - -static void -list_store_start_element (GtkBuildableParseContext *context, - const char *element_name, - const char **names, - const char **values, - gpointer user_data, - GError **error) -{ - SubParserData *data = (SubParserData*)user_data; - - if (strcmp (element_name, "col") == 0) - { - int id = -1; - const char *id_str; - const char *msg_context = NULL; - gboolean translatable = FALSE; - ColInfo *info; - GValue val = G_VALUE_INIT; - - if (!_gtk_builder_check_parent (data->builder, context, "row", error)) - return; - - if (data->row_column >= data->n_columns) - { - g_set_error (error, - GTK_BUILDER_ERROR, GTK_BUILDER_ERROR_INVALID_VALUE, - "Too many columns, maximum is %d", data->n_columns - 1); - _gtk_builder_prefix_error (data->builder, context, error); - return; - } - - if (!g_markup_collect_attributes (element_name, names, values, error, - G_MARKUP_COLLECT_STRING, "id", &id_str, - G_MARKUP_COLLECT_BOOLEAN|G_MARKUP_COLLECT_OPTIONAL, "translatable", &translatable, - G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "comments", NULL, - G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "context", &msg_context, - G_MARKUP_COLLECT_INVALID)) - { - _gtk_builder_prefix_error (data->builder, context, error); - return; - } - - if (!gtk_builder_value_from_string_type (data->builder, G_TYPE_INT, id_str, &val, error)) - { - _gtk_builder_prefix_error (data->builder, context, error); - return; - } - - id = g_value_get_int (&val); - if (id < 0 || id >= data->n_columns) - { - g_set_error (error, - GTK_BUILDER_ERROR, GTK_BUILDER_ERROR_INVALID_VALUE, - "id value %d out of range", id); - _gtk_builder_prefix_error (data->builder, context, error); - return; - } - - info = g_slice_new0 (ColInfo); - info->translatable = translatable; - info->context = g_strdup (msg_context); - info->id = id; - - data->colids[data->row_column] = id; - data->columns[data->row_column] = info; - data->row_column++; - data->is_data = TRUE; - } - else if (strcmp (element_name, "row") == 0) - { - if (!_gtk_builder_check_parent (data->builder, context, "data", error)) - return; - - if (!g_markup_collect_attributes (element_name, names, values, error, - G_MARKUP_COLLECT_INVALID, NULL, NULL, - G_MARKUP_COLLECT_INVALID)) - _gtk_builder_prefix_error (data->builder, context, error); - } - else if (strcmp (element_name, "columns") == 0 || - strcmp (element_name, "data") == 0) - { - if (!_gtk_builder_check_parent (data->builder, context, "object", error)) - return; - - if (!g_markup_collect_attributes (element_name, names, values, error, - G_MARKUP_COLLECT_INVALID, NULL, NULL, - G_MARKUP_COLLECT_INVALID)) - _gtk_builder_prefix_error (data->builder, context, error); - } - else if (strcmp (element_name, "column") == 0) - { - const char *type; - - if (!_gtk_builder_check_parent (data->builder, context, "columns", error)) - return; - - if (!g_markup_collect_attributes (element_name, names, values, error, - G_MARKUP_COLLECT_STRING, "type", &type, - G_MARKUP_COLLECT_INVALID)) - { - _gtk_builder_prefix_error (data->builder, context, error); - return; - } - - data->column_type_names = g_slist_prepend (data->column_type_names, g_strdup (type)); - } - else - { - _gtk_builder_error_unhandled_tag (data->builder, context, - "GtkListStore", element_name, - error); - } -} - -static void -list_store_end_element (GtkBuildableParseContext *context, - const char *element_name, - gpointer user_data, - GError **error) -{ - SubParserData *data = (SubParserData*)user_data; - - g_assert (data->builder); - - if (strcmp (element_name, "row") == 0) - { - GtkTreeIter iter; - int i; - - gtk_list_store_insert_with_valuesv (GTK_LIST_STORE (data->object), - &iter, - data->last_row, - data->colids, - data->values, - data->row_column); - for (i = 0; i < data->row_column; i++) - { - ColInfo *info = data->columns[i]; - g_free (info->context); - g_slice_free (ColInfo, info); - data->columns[i] = NULL; - g_value_unset (&data->values[i]); - } - g_free (data->values); - data->values = g_new0 (GValue, data->n_columns); - data->last_row++; - data->row_column = 0; - } - else if (strcmp (element_name, "columns") == 0) - { - GType *column_types; - GSList *l; - int i; - GType type; - - data->column_type_names = g_slist_reverse (data->column_type_names); - column_types = g_new0 (GType, g_slist_length (data->column_type_names)); - - for (l = data->column_type_names, i = 0; l; l = l->next, i++) - { - type = gtk_builder_get_type_from_name (data->builder, l->data); - if (type == G_TYPE_INVALID) - { - g_warning ("Unknown type %s specified in treemodel %s", - (const char *)l->data, - gtk_buildable_get_buildable_id (GTK_BUILDABLE (data->object))); - continue; - } - column_types[i] = type; - - g_free (l->data); - } - - gtk_list_store_set_column_types (GTK_LIST_STORE (data->object), i, column_types); - - g_free (column_types); - } - else if (strcmp (element_name, "col") == 0) - { - data->is_data = FALSE; - } -} - -static void -list_store_text (GtkBuildableParseContext *context, - const char *text, - gsize text_len, - gpointer user_data, - GError **error) -{ - SubParserData *data = (SubParserData*)user_data; - int i; - char *string; - ColInfo *info; - - if (!data->is_data) - return; - - i = data->row_column - 1; - info = data->columns[i]; - - string = g_strndup (text, text_len); - if (info->translatable && text_len) - { - char *translated; - - /* FIXME: This will not use the domain set in the .ui file, - * since the parser is not telling the builder about the domain. - * However, it will work for gtk_builder_set_translation_domain() calls. - */ - translated = g_strdup (_gtk_builder_parser_translate (data->domain, - info->context, - string)); - g_free (string); - string = translated; - } - - if (!gtk_builder_value_from_string_type (data->builder, - data->column_types[info->id], - string, - &data->values[i], - error)) - { - _gtk_builder_prefix_error (data->builder, context, error); - } - g_free (string); -} - -static const GtkBuildableParser list_store_parser = - { - list_store_start_element, - list_store_end_element, - list_store_text - }; - -static gboolean -gtk_list_store_buildable_custom_tag_start (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const char *tagname, - GtkBuildableParser *parser, - gpointer *parser_data) -{ - SubParserData *data; - - if (child) - return FALSE; - - if (strcmp (tagname, "columns") == 0) - { - data = g_slice_new0 (SubParserData); - data->builder = builder; - data->object = G_OBJECT (buildable); - data->column_type_names = NULL; - - *parser = list_store_parser; - *parser_data = data; - - return TRUE; - } - else if (strcmp (tagname, "data") == 0) - { - int n_columns = gtk_list_store_get_n_columns (GTK_TREE_MODEL (buildable)); - if (n_columns == 0) - g_error ("Cannot append data to an empty model"); - - data = g_slice_new0 (SubParserData); - data->builder = builder; - data->object = G_OBJECT (buildable); - data->values = g_new0 (GValue, n_columns); - data->colids = g_new0 (int, n_columns); - data->columns = g_new0 (ColInfo*, n_columns); - data->column_types = GTK_LIST_STORE (buildable)->priv->column_headers; - data->n_columns = n_columns; - data->last_row = 0; - data->domain = gtk_builder_get_translation_domain (builder); - - *parser = list_store_parser; - *parser_data = data; - - return TRUE; - } - - return FALSE; -} - -static void -gtk_list_store_buildable_custom_tag_end (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const char *tagname, - gpointer parser_data) -{ - SubParserData *data = (SubParserData*)parser_data; - - if (strcmp (tagname, "columns") == 0) - { - g_slist_free (data->column_type_names); - g_slice_free (SubParserData, data); - } - else if (strcmp (tagname, "data") == 0) - { - int i; - for (i = 0; i < data->n_columns; i++) - { - ColInfo *info = data->columns[i]; - if (info) - { - g_free (info->context); - g_slice_free (ColInfo, info); - } - } - g_free (data->colids); - g_free (data->columns); - g_free (data->values); - g_slice_free (SubParserData, data); - } -} diff --git a/gtk/gtkliststore.h b/gtk/gtkliststore.h deleted file mode 100644 index a83871e87b..0000000000 --- a/gtk/gtkliststore.h +++ /dev/null @@ -1,154 +0,0 @@ -/* gtkliststore.h - * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#ifndef __GTK_LIST_STORE_H__ -#define __GTK_LIST_STORE_H__ - -#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) -#error "Only can be included directly." -#endif - -#include -#include -#include - - -G_BEGIN_DECLS - - -#define GTK_TYPE_LIST_STORE (gtk_list_store_get_type ()) -#define GTK_LIST_STORE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_LIST_STORE, GtkListStore)) -#define GTK_LIST_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_LIST_STORE, GtkListStoreClass)) -#define GTK_IS_LIST_STORE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_LIST_STORE)) -#define GTK_IS_LIST_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_LIST_STORE)) -#define GTK_LIST_STORE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_LIST_STORE, GtkListStoreClass)) - -typedef struct _GtkListStore GtkListStore; -typedef struct _GtkListStorePrivate GtkListStorePrivate; -typedef struct _GtkListStoreClass GtkListStoreClass; - -struct _GtkListStore -{ - GObject parent; - - /*< private >*/ - GtkListStorePrivate *priv; -}; - -struct _GtkListStoreClass -{ - GObjectClass parent_class; - - /*< private >*/ - gpointer padding[8]; -}; - - -GDK_AVAILABLE_IN_ALL -GType gtk_list_store_get_type (void) G_GNUC_CONST; -GDK_AVAILABLE_IN_ALL -GtkListStore *gtk_list_store_new (int n_columns, - ...); -GDK_AVAILABLE_IN_ALL -GtkListStore *gtk_list_store_newv (int n_columns, - GType *types); -GDK_AVAILABLE_IN_ALL -void gtk_list_store_set_column_types (GtkListStore *list_store, - int n_columns, - GType *types); - -/* NOTE: use gtk_tree_model_get to get values from a GtkListStore */ - -GDK_AVAILABLE_IN_ALL -void gtk_list_store_set_value (GtkListStore *list_store, - GtkTreeIter *iter, - int column, - GValue *value); -GDK_AVAILABLE_IN_ALL -void gtk_list_store_set (GtkListStore *list_store, - GtkTreeIter *iter, - ...); -GDK_AVAILABLE_IN_ALL -void gtk_list_store_set_valuesv (GtkListStore *list_store, - GtkTreeIter *iter, - int *columns, - GValue *values, - int n_values); -GDK_AVAILABLE_IN_ALL -void gtk_list_store_set_valist (GtkListStore *list_store, - GtkTreeIter *iter, - va_list var_args); -GDK_AVAILABLE_IN_ALL -gboolean gtk_list_store_remove (GtkListStore *list_store, - GtkTreeIter *iter); -GDK_AVAILABLE_IN_ALL -void gtk_list_store_insert (GtkListStore *list_store, - GtkTreeIter *iter, - int position); -GDK_AVAILABLE_IN_ALL -void gtk_list_store_insert_before (GtkListStore *list_store, - GtkTreeIter *iter, - GtkTreeIter *sibling); -GDK_AVAILABLE_IN_ALL -void gtk_list_store_insert_after (GtkListStore *list_store, - GtkTreeIter *iter, - GtkTreeIter *sibling); -GDK_AVAILABLE_IN_ALL -void gtk_list_store_insert_with_values (GtkListStore *list_store, - GtkTreeIter *iter, - int position, - ...); -GDK_AVAILABLE_IN_ALL -void gtk_list_store_insert_with_valuesv (GtkListStore *list_store, - GtkTreeIter *iter, - int position, - int *columns, - GValue *values, - int n_values); -GDK_AVAILABLE_IN_ALL -void gtk_list_store_prepend (GtkListStore *list_store, - GtkTreeIter *iter); -GDK_AVAILABLE_IN_ALL -void gtk_list_store_append (GtkListStore *list_store, - GtkTreeIter *iter); -GDK_AVAILABLE_IN_ALL -void gtk_list_store_clear (GtkListStore *list_store); -GDK_AVAILABLE_IN_ALL -gboolean gtk_list_store_iter_is_valid (GtkListStore *list_store, - GtkTreeIter *iter); -GDK_AVAILABLE_IN_ALL -void gtk_list_store_reorder (GtkListStore *store, - int *new_order); -GDK_AVAILABLE_IN_ALL -void gtk_list_store_swap (GtkListStore *store, - GtkTreeIter *a, - GtkTreeIter *b); -GDK_AVAILABLE_IN_ALL -void gtk_list_store_move_after (GtkListStore *store, - GtkTreeIter *iter, - GtkTreeIter *position); -GDK_AVAILABLE_IN_ALL -void gtk_list_store_move_before (GtkListStore *store, - GtkTreeIter *iter, - GtkTreeIter *position); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkListStore, g_object_unref) - -G_END_DECLS - - -#endif /* __GTK_LIST_STORE_H__ */ diff --git a/gtk/gtkmountoperation.c b/gtk/gtkmountoperation.c index 5b41767e54..aeca6e7253 100644 --- a/gtk/gtkmountoperation.c +++ b/gtk/gtkmountoperation.c @@ -49,7 +49,6 @@ #include "gtkpopover.h" #include "gtksnapshot.h" #include "gdktextureprivate.h" -#include "gtkliststore.h" #include #include "gtklistview.h" #include "gtksignallistitemfactory.h" diff --git a/gtk/gtkpagesetupunixdialog.c b/gtk/gtkpagesetupunixdialog.c index 80fc561237..399d38f29d 100644 --- a/gtk/gtkpagesetupunixdialog.c +++ b/gtk/gtkpagesetupunixdialog.c @@ -29,8 +29,6 @@ #include "gtkcheckbutton.h" #include "gtklabel.h" #include "gtkgrid.h" -#include "gtkcelllayout.h" -#include "gtkcellrenderertext.h" #include "gtkpagesetupunixdialog.h" #include "gtkcustompaperunixdialog.h" diff --git a/gtk/gtkplacessidebar.c b/gtk/gtkplacessidebar.c index b2fb2aaeb8..e632b62511 100644 --- a/gtk/gtkplacessidebar.c +++ b/gtk/gtkplacessidebar.c @@ -32,7 +32,6 @@ #include "gtksidebarrowprivate.h" #include "gdk/gdkkeysyms.h" #include "gtkbookmarksmanagerprivate.h" -#include "gtkcelllayout.h" #include "gtkfilechooserutils.h" #include "gtkicontheme.h" #include diff --git a/gtk/gtkplacesview.c b/gtk/gtkplacesview.c index 0c58880681..9684b0b0e8 100644 --- a/gtk/gtkplacesview.c +++ b/gtk/gtkplacesview.c @@ -520,6 +520,8 @@ on_remove_server_button_clicked (RemoveServerData *data) populate_servers (data->view); } +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + static void populate_servers (GtkPlacesView *view) { @@ -615,6 +617,8 @@ populate_servers (GtkPlacesView *view) g_bookmark_file_free (server_list); } +G_GNUC_END_IGNORE_DEPRECATIONS + static void update_view_mode (GtkPlacesView *view) { diff --git a/gtk/gtkprinteroptionwidget.c b/gtk/gtkprinteroptionwidget.c index 4737171ba5..ffcd1991e7 100644 --- a/gtk/gtkprinteroptionwidget.c +++ b/gtk/gtkprinteroptionwidget.c @@ -31,7 +31,6 @@ #include "gtkfilechooserprivate.h" #include "gtkimage.h" #include "gtklabel.h" -#include "gtkliststore.h" #include "gtkcheckbutton.h" #include "gtkgrid.h" #include "gtkorientable.h" diff --git a/gtk/gtksearchenginemodel.c b/gtk/gtksearchenginemodel.c index 83ea00bdb0..3923ea6813 100644 --- a/gtk/gtksearchenginemodel.c +++ b/gtk/gtksearchenginemodel.c @@ -28,6 +28,8 @@ #include +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + struct _GtkSearchEngineModel { GtkSearchEngine parent; diff --git a/gtk/gtkspinbutton.c b/gtk/gtkspinbutton.c index 7b2074b655..8ea2a1c537 100644 --- a/gtk/gtkspinbutton.c +++ b/gtk/gtkspinbutton.c @@ -38,7 +38,7 @@ #include "gtkbutton.h" #include "gtkbuttonprivate.h" #include "gtkeditable.h" -#include "gtkcelleditable.h" +#include "deprecated/gtkcelleditable.h" #include "gtkimage.h" #include "gtktext.h" #include "gtkeventcontrollerkey.h" @@ -651,6 +651,8 @@ gtk_spin_button_accessible_range_init (GtkAccessibleRangeInterface *iface) iface->set_current_value = accessible_range_set_current_value; } +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + static void gtk_cell_editable_spin_button_activated (GtkText *text, GtkSpinButton *spin) { @@ -711,6 +713,8 @@ gtk_spin_button_cell_editable_init (GtkCellEditableIface *iface) iface->start_editing = gtk_spin_button_start_editing; } +G_GNUC_END_IGNORE_DEPRECATIONS + static void gtk_spin_button_set_property (GObject *object, guint prop_id, diff --git a/gtk/gtktreedatalist.c b/gtk/gtktreedatalist.c deleted file mode 100644 index 4e35edbe56..0000000000 --- a/gtk/gtktreedatalist.c +++ /dev/null @@ -1,572 +0,0 @@ -/* gtktreedatalist.c - * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - * - * This file contains code shared between GtkTreeStore and GtkListStore. Please - * do not use it. - */ - -#include "config.h" -#include "gtktreedatalistprivate.h" -#include - -/* node allocation - */ -GtkTreeDataList * -_gtk_tree_data_list_alloc (void) -{ - GtkTreeDataList *list; - - list = g_slice_new0 (GtkTreeDataList); - - return list; -} - -void -_gtk_tree_data_list_free (GtkTreeDataList *list, - GType *column_headers) -{ - GtkTreeDataList *tmp, *next; - int i = 0; - - tmp = list; - - while (tmp) - { - next = tmp->next; - if (g_type_is_a (column_headers [i], G_TYPE_STRING)) - g_free ((char *) tmp->data.v_pointer); - else if (g_type_is_a (column_headers [i], G_TYPE_OBJECT) && tmp->data.v_pointer != NULL) - g_object_unref (tmp->data.v_pointer); - else if (g_type_is_a (column_headers [i], G_TYPE_BOXED) && tmp->data.v_pointer != NULL) - g_boxed_free (column_headers [i], (gpointer) tmp->data.v_pointer); - else if (g_type_is_a (column_headers [i], G_TYPE_VARIANT) && tmp->data.v_pointer != NULL) - g_variant_unref ((gpointer) tmp->data.v_pointer); - - g_slice_free (GtkTreeDataList, tmp); - i++; - tmp = next; - } -} - -gboolean -_gtk_tree_data_list_check_type (GType type) -{ - int i = 0; - static const GType type_list[] = - { - G_TYPE_BOOLEAN, - G_TYPE_CHAR, - G_TYPE_UCHAR, - G_TYPE_INT, - G_TYPE_UINT, - G_TYPE_LONG, - G_TYPE_ULONG, - G_TYPE_INT64, - G_TYPE_UINT64, - G_TYPE_ENUM, - G_TYPE_FLAGS, - G_TYPE_FLOAT, - G_TYPE_DOUBLE, - G_TYPE_STRING, - G_TYPE_POINTER, - G_TYPE_BOXED, - G_TYPE_OBJECT, - G_TYPE_VARIANT, - G_TYPE_INVALID - }; - - if (! G_TYPE_IS_VALUE_TYPE (type)) - return FALSE; - - - while (type_list[i] != G_TYPE_INVALID) - { - if (g_type_is_a (type, type_list[i])) - return TRUE; - i++; - } - return FALSE; -} - -static inline GType -get_fundamental_type (GType type) -{ - GType result; - - result = G_TYPE_FUNDAMENTAL (type); - - if (result == G_TYPE_INTERFACE) - { - if (g_type_is_a (type, G_TYPE_OBJECT)) - result = G_TYPE_OBJECT; - } - - return result; -} -void -_gtk_tree_data_list_node_to_value (GtkTreeDataList *list, - GType type, - GValue *value) -{ - g_value_init (value, type); - - switch (get_fundamental_type (type)) - { - case G_TYPE_BOOLEAN: - g_value_set_boolean (value, (gboolean) list->data.v_int); - break; - case G_TYPE_CHAR: - g_value_set_schar (value, (char) list->data.v_char); - break; - case G_TYPE_UCHAR: - g_value_set_uchar (value, (guchar) list->data.v_uchar); - break; - case G_TYPE_INT: - g_value_set_int (value, (int) list->data.v_int); - break; - case G_TYPE_UINT: - g_value_set_uint (value, (guint) list->data.v_uint); - break; - case G_TYPE_LONG: - g_value_set_long (value, list->data.v_long); - break; - case G_TYPE_ULONG: - g_value_set_ulong (value, list->data.v_ulong); - break; - case G_TYPE_INT64: - g_value_set_int64 (value, list->data.v_int64); - break; - case G_TYPE_UINT64: - g_value_set_uint64 (value, list->data.v_uint64); - break; - case G_TYPE_ENUM: - g_value_set_enum (value, list->data.v_int); - break; - case G_TYPE_FLAGS: - g_value_set_flags (value, list->data.v_uint); - break; - case G_TYPE_FLOAT: - g_value_set_float (value, (float) list->data.v_float); - break; - case G_TYPE_DOUBLE: - g_value_set_double (value, (double) list->data.v_double); - break; - case G_TYPE_STRING: - g_value_set_string (value, (char *) list->data.v_pointer); - break; - case G_TYPE_POINTER: - g_value_set_pointer (value, (gpointer) list->data.v_pointer); - break; - case G_TYPE_BOXED: - g_value_set_boxed (value, (gpointer) list->data.v_pointer); - break; - case G_TYPE_VARIANT: - g_value_set_variant (value, (gpointer) list->data.v_pointer); - break; - case G_TYPE_OBJECT: - g_value_set_object (value, (GObject *) list->data.v_pointer); - break; - default: - g_warning ("%s: Unsupported type (%s) retrieved.", G_STRLOC, g_type_name (value->g_type)); - break; - } -} - -void -_gtk_tree_data_list_value_to_node (GtkTreeDataList *list, - GValue *value) -{ - switch (get_fundamental_type (G_VALUE_TYPE (value))) - { - case G_TYPE_BOOLEAN: - list->data.v_int = g_value_get_boolean (value); - break; - case G_TYPE_CHAR: - list->data.v_char = g_value_get_schar (value); - break; - case G_TYPE_UCHAR: - list->data.v_uchar = g_value_get_uchar (value); - break; - case G_TYPE_INT: - list->data.v_int = g_value_get_int (value); - break; - case G_TYPE_UINT: - list->data.v_uint = g_value_get_uint (value); - break; - case G_TYPE_LONG: - list->data.v_long = g_value_get_long (value); - break; - case G_TYPE_ULONG: - list->data.v_ulong = g_value_get_ulong (value); - break; - case G_TYPE_INT64: - list->data.v_int64 = g_value_get_int64 (value); - break; - case G_TYPE_UINT64: - list->data.v_uint64 = g_value_get_uint64 (value); - break; - case G_TYPE_ENUM: - list->data.v_int = g_value_get_enum (value); - break; - case G_TYPE_FLAGS: - list->data.v_uint = g_value_get_flags (value); - break; - case G_TYPE_POINTER: - list->data.v_pointer = g_value_get_pointer (value); - break; - case G_TYPE_FLOAT: - list->data.v_float = g_value_get_float (value); - break; - case G_TYPE_DOUBLE: - list->data.v_double = g_value_get_double (value); - break; - case G_TYPE_STRING: - g_free (list->data.v_pointer); - list->data.v_pointer = g_value_dup_string (value); - break; - case G_TYPE_OBJECT: - if (list->data.v_pointer) - g_object_unref (list->data.v_pointer); - list->data.v_pointer = g_value_dup_object (value); - break; - case G_TYPE_BOXED: - if (list->data.v_pointer) - g_boxed_free (G_VALUE_TYPE (value), list->data.v_pointer); - list->data.v_pointer = g_value_dup_boxed (value); - break; - case G_TYPE_VARIANT: - if (list->data.v_pointer) - g_variant_unref (list->data.v_pointer); - list->data.v_pointer = g_value_dup_variant (value); - break; - default: - g_warning ("%s: Unsupported type (%s) stored.", G_STRLOC, g_type_name (G_VALUE_TYPE (value))); - break; - } -} - -GtkTreeDataList * -_gtk_tree_data_list_node_copy (GtkTreeDataList *list, - GType type) -{ - GtkTreeDataList *new_list; - - g_return_val_if_fail (list != NULL, NULL); - - new_list = _gtk_tree_data_list_alloc (); - new_list->next = NULL; - - switch (get_fundamental_type (type)) - { - case G_TYPE_BOOLEAN: - case G_TYPE_CHAR: - case G_TYPE_UCHAR: - case G_TYPE_INT: - case G_TYPE_UINT: - case G_TYPE_LONG: - case G_TYPE_ULONG: - case G_TYPE_INT64: - case G_TYPE_UINT64: - case G_TYPE_ENUM: - case G_TYPE_FLAGS: - case G_TYPE_POINTER: - case G_TYPE_FLOAT: - case G_TYPE_DOUBLE: - new_list->data = list->data; - break; - case G_TYPE_STRING: - new_list->data.v_pointer = g_strdup (list->data.v_pointer); - break; - case G_TYPE_OBJECT: - case G_TYPE_INTERFACE: - new_list->data.v_pointer = list->data.v_pointer; - if (new_list->data.v_pointer) - g_object_ref (new_list->data.v_pointer); - break; - case G_TYPE_BOXED: - if (list->data.v_pointer) - new_list->data.v_pointer = g_boxed_copy (type, list->data.v_pointer); - else - new_list->data.v_pointer = NULL; - break; - case G_TYPE_VARIANT: - if (list->data.v_pointer) - new_list->data.v_pointer = g_variant_ref (list->data.v_pointer); - else - new_list->data.v_pointer = NULL; - break; - default: - g_warning ("Unsupported node type (%s) copied.", g_type_name (type)); - break; - } - - return new_list; -} - -int -_gtk_tree_data_list_compare_func (GtkTreeModel *model, - GtkTreeIter *a, - GtkTreeIter *b, - gpointer user_data) -{ - int column = GPOINTER_TO_INT (user_data); - GType type = gtk_tree_model_get_column_type (model, column); - GValue a_value = G_VALUE_INIT; - GValue b_value = G_VALUE_INIT; - int retval; - const char *stra, *strb; - - gtk_tree_model_get_value (model, a, column, &a_value); - gtk_tree_model_get_value (model, b, column, &b_value); - - switch (get_fundamental_type (type)) - { - case G_TYPE_BOOLEAN: - if (g_value_get_boolean (&a_value) < g_value_get_boolean (&b_value)) - retval = -1; - else if (g_value_get_boolean (&a_value) == g_value_get_boolean (&b_value)) - retval = 0; - else - retval = 1; - break; - case G_TYPE_CHAR: - if (g_value_get_schar (&a_value) < g_value_get_schar (&b_value)) - retval = -1; - else if (g_value_get_schar (&a_value) == g_value_get_schar (&b_value)) - retval = 0; - else - retval = 1; - break; - case G_TYPE_UCHAR: - if (g_value_get_uchar (&a_value) < g_value_get_uchar (&b_value)) - retval = -1; - else if (g_value_get_uchar (&a_value) == g_value_get_uchar (&b_value)) - retval = 0; - else - retval = 1; - break; - case G_TYPE_INT: - if (g_value_get_int (&a_value) < g_value_get_int (&b_value)) - retval = -1; - else if (g_value_get_int (&a_value) == g_value_get_int (&b_value)) - retval = 0; - else - retval = 1; - break; - case G_TYPE_UINT: - if (g_value_get_uint (&a_value) < g_value_get_uint (&b_value)) - retval = -1; - else if (g_value_get_uint (&a_value) == g_value_get_uint (&b_value)) - retval = 0; - else - retval = 1; - break; - case G_TYPE_LONG: - if (g_value_get_long (&a_value) < g_value_get_long (&b_value)) - retval = -1; - else if (g_value_get_long (&a_value) == g_value_get_long (&b_value)) - retval = 0; - else - retval = 1; - break; - case G_TYPE_ULONG: - if (g_value_get_ulong (&a_value) < g_value_get_ulong (&b_value)) - retval = -1; - else if (g_value_get_ulong (&a_value) == g_value_get_ulong (&b_value)) - retval = 0; - else - retval = 1; - break; - case G_TYPE_INT64: - if (g_value_get_int64 (&a_value) < g_value_get_int64 (&b_value)) - retval = -1; - else if (g_value_get_int64 (&a_value) == g_value_get_int64 (&b_value)) - retval = 0; - else - retval = 1; - break; - case G_TYPE_UINT64: - if (g_value_get_uint64 (&a_value) < g_value_get_uint64 (&b_value)) - retval = -1; - else if (g_value_get_uint64 (&a_value) == g_value_get_uint64 (&b_value)) - retval = 0; - else - retval = 1; - break; - case G_TYPE_ENUM: - /* this is somewhat bogus. */ - if (g_value_get_enum (&a_value) < g_value_get_enum (&b_value)) - retval = -1; - else if (g_value_get_enum (&a_value) == g_value_get_enum (&b_value)) - retval = 0; - else - retval = 1; - break; - case G_TYPE_FLAGS: - /* this is even more bogus. */ - if (g_value_get_flags (&a_value) < g_value_get_flags (&b_value)) - retval = -1; - else if (g_value_get_flags (&a_value) == g_value_get_flags (&b_value)) - retval = 0; - else - retval = 1; - break; - case G_TYPE_FLOAT: - if (g_value_get_float (&a_value) < g_value_get_float (&b_value)) - retval = -1; - else if (g_value_get_float (&a_value) == g_value_get_float (&b_value)) - retval = 0; - else - retval = 1; - break; - case G_TYPE_DOUBLE: - if (g_value_get_double (&a_value) < g_value_get_double (&b_value)) - retval = -1; - else if (g_value_get_double (&a_value) == g_value_get_double (&b_value)) - retval = 0; - else - retval = 1; - break; - case G_TYPE_STRING: - stra = g_value_get_string (&a_value); - strb = g_value_get_string (&b_value); - if (stra == NULL) stra = ""; - if (strb == NULL) strb = ""; - retval = g_utf8_collate (stra, strb); - break; - case G_TYPE_VARIANT: - case G_TYPE_POINTER: - case G_TYPE_BOXED: - case G_TYPE_OBJECT: - default: - g_warning ("Attempting to sort on invalid type %s", g_type_name (type)); - retval = FALSE; - break; - } - - g_value_unset (&a_value); - g_value_unset (&b_value); - - return retval; -} - - -GList * -_gtk_tree_data_list_header_new (int n_columns, - GType *types) -{ - GList *retval = NULL; - - int i; - - for (i = 0; i < n_columns; i ++) - { - GtkTreeDataSortHeader *header; - - header = g_slice_new (GtkTreeDataSortHeader); - - retval = g_list_prepend (retval, header); - header->sort_column_id = i; - header->func = _gtk_tree_data_list_compare_func; - header->destroy = NULL; - header->data = GINT_TO_POINTER (i); - } - return g_list_reverse (retval); -} - -void -_gtk_tree_data_list_header_free (GList *list) -{ - GList *tmp; - - for (tmp = list; tmp; tmp = tmp->next) - { - GtkTreeDataSortHeader *header = (GtkTreeDataSortHeader *) tmp->data; - - if (header->destroy) - { - GDestroyNotify d = header->destroy; - - header->destroy = NULL; - d (header->data); - } - - g_slice_free (GtkTreeDataSortHeader, header); - } - g_list_free (list); -} - -GtkTreeDataSortHeader * -_gtk_tree_data_list_get_header (GList *header_list, - int sort_column_id) -{ - GtkTreeDataSortHeader *header = NULL; - - for (; header_list; header_list = header_list->next) - { - header = (GtkTreeDataSortHeader*) header_list->data; - if (header->sort_column_id == sort_column_id) - return header; - } - return NULL; -} - - -GList * -_gtk_tree_data_list_set_header (GList *header_list, - int sort_column_id, - GtkTreeIterCompareFunc func, - gpointer data, - GDestroyNotify destroy) -{ - GList *list = header_list; - GtkTreeDataSortHeader *header = NULL; - - for (; list; list = list->next) - { - header = (GtkTreeDataSortHeader*) list->data; - if (header->sort_column_id == sort_column_id) - break; - header = NULL; - - if (list->next == NULL) - break; - } - - if (header == NULL) - { - header = g_slice_new0 (GtkTreeDataSortHeader); - header->sort_column_id = sort_column_id; - if (list) - list = g_list_append (list, header); - else - header_list = g_list_append (header_list, header); - } - - if (header->destroy) - { - GDestroyNotify d = header->destroy; - - header->destroy = NULL; - d (header->data); - } - - header->func = func; - header->data = data; - header->destroy = destroy; - - return header_list; -} diff --git a/gtk/gtktreedatalistprivate.h b/gtk/gtktreedatalistprivate.h deleted file mode 100644 index 77aa710961..0000000000 --- a/gtk/gtktreedatalistprivate.h +++ /dev/null @@ -1,81 +0,0 @@ -/* gtktreedatalist.h - * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#ifndef __GTK_TREE_DATA_LIST_PRIVATE_H__ -#define __GTK_TREE_DATA_LIST_PRIVATE_H__ - -#include -#include - -typedef struct _GtkTreeDataList GtkTreeDataList; -struct _GtkTreeDataList -{ - GtkTreeDataList *next; - - union { - int v_int; - gint8 v_char; - guint8 v_uchar; - guint v_uint; - glong v_long; - gulong v_ulong; - gint64 v_int64; - guint64 v_uint64; - float v_float; - double v_double; - gpointer v_pointer; - } data; -}; - -typedef struct _GtkTreeDataSortHeader -{ - int sort_column_id; - GtkTreeIterCompareFunc func; - gpointer data; - GDestroyNotify destroy; -} GtkTreeDataSortHeader; - -GtkTreeDataList *_gtk_tree_data_list_alloc (void); -void _gtk_tree_data_list_free (GtkTreeDataList *list, - GType *column_headers); -gboolean _gtk_tree_data_list_check_type (GType type); -void _gtk_tree_data_list_node_to_value (GtkTreeDataList *list, - GType type, - GValue *value); -void _gtk_tree_data_list_value_to_node (GtkTreeDataList *list, - GValue *value); - -GtkTreeDataList *_gtk_tree_data_list_node_copy (GtkTreeDataList *list, - GType type); - -/* Header code */ -int _gtk_tree_data_list_compare_func (GtkTreeModel *model, - GtkTreeIter *a, - GtkTreeIter *b, - gpointer user_data); -GList * _gtk_tree_data_list_header_new (int n_columns, - GType *types); -void _gtk_tree_data_list_header_free (GList *header_list); -GtkTreeDataSortHeader *_gtk_tree_data_list_get_header (GList *header_list, - int sort_column_id); -GList *_gtk_tree_data_list_set_header (GList *header_list, - int sort_column_id, - GtkTreeIterCompareFunc func, - gpointer data, - GDestroyNotify destroy); - -#endif /* __GTK_TREE_DATA_LIST_PRIVATE_H__ */ diff --git a/gtk/gtktreednd.c b/gtk/gtktreednd.c deleted file mode 100644 index d4816cd1b2..0000000000 --- a/gtk/gtktreednd.c +++ /dev/null @@ -1,356 +0,0 @@ -/* gtktreednd.c - * Copyright (C) 2001 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#include "config.h" -#include -#include "gtktreednd.h" - -#include "gtkprivate.h" - -/** - * SECTION:gtktreednd - * @Short_description: Interfaces for drag-and-drop support in GtkTreeView - * @Title: GtkTreeView drag-and-drop - * - * GTK supports Drag-and-Drop in tree views with a high-level and a low-level - * API. - * - * The low-level API consists of the GTK DND API, augmented by some treeview - * utility functions: gtk_tree_view_set_drag_dest_row(), - * gtk_tree_view_get_drag_dest_row(), gtk_tree_view_get_dest_row_at_pos(), - * gtk_tree_view_create_row_drag_icon(), gtk_tree_set_row_drag_data() and - * gtk_tree_get_row_drag_data(). This API leaves a lot of flexibility, but - * nothing is done automatically, and implementing advanced features like - * hover-to-open-rows or autoscrolling on top of this API is a lot of work. - * - * On the other hand, if you write to the high-level API, then all the - * bookkeeping of rows is done for you, as well as things like hover-to-open - * and auto-scroll, but your models have to implement the - * `GtkTreeDragSource` and `GtkTreeDragDest` interfaces. - */ - -/** - * GtkTreeDragDest: - * - * Interface for Drag-and-Drop destinations in `GtkTreeView`. - */ - -/** - * GtkTreeDragSource: - * - * Interface for Drag-and-Drop destinations in `GtkTreeView`. - */ - -GType -gtk_tree_drag_source_get_type (void) -{ - static GType our_type = 0; - - if (!our_type) - { - const GTypeInfo our_info = - { - sizeof (GtkTreeDragSourceIface), /* class_size */ - NULL, /* base_init */ - NULL, /* base_finalize */ - NULL, - NULL, /* class_finalize */ - NULL, /* class_data */ - 0, - 0, /* n_preallocs */ - NULL - }; - - our_type = g_type_register_static (G_TYPE_INTERFACE, - I_("GtkTreeDragSource"), - &our_info, 0); - } - - return our_type; -} - - -GType -gtk_tree_drag_dest_get_type (void) -{ - static GType our_type = 0; - - if (!our_type) - { - const GTypeInfo our_info = - { - sizeof (GtkTreeDragDestIface), /* class_size */ - NULL, /* base_init */ - NULL, /* base_finalize */ - NULL, - NULL, /* class_finalize */ - NULL, /* class_data */ - 0, - 0, /* n_preallocs */ - NULL - }; - - our_type = g_type_register_static (G_TYPE_INTERFACE, I_("GtkTreeDragDest"), &our_info, 0); - } - - return our_type; -} - -/** - * gtk_tree_drag_source_row_draggable: - * @drag_source: a `GtkTreeDragSource` - * @path: row on which user is initiating a drag - * - * Asks the `GtkTreeDragSource` whether a particular row can be used as - * the source of a DND operation. If the source doesn’t implement - * this interface, the row is assumed draggable. - * - * Returns: %TRUE if the row can be dragged - **/ -gboolean -gtk_tree_drag_source_row_draggable (GtkTreeDragSource *drag_source, - GtkTreePath *path) -{ - GtkTreeDragSourceIface *iface = GTK_TREE_DRAG_SOURCE_GET_IFACE (drag_source); - - g_return_val_if_fail (path != NULL, FALSE); - - if (iface->row_draggable) - return (* iface->row_draggable) (drag_source, path); - else - return TRUE; - /* Returning TRUE if row_draggable is not implemented is a fallback. - Interface implementations such as GtkTreeStore and GtkListStore really should - implement row_draggable. */ -} - - -/** - * gtk_tree_drag_source_drag_data_delete: - * @drag_source: a `GtkTreeDragSource` - * @path: row that was being dragged - * - * Asks the `GtkTreeDragSource` to delete the row at @path, because - * it was moved somewhere else via drag-and-drop. Returns %FALSE - * if the deletion fails because @path no longer exists, or for - * some model-specific reason. Should robustly handle a @path no - * longer found in the model! - * - * Returns: %TRUE if the row was successfully deleted - **/ -gboolean -gtk_tree_drag_source_drag_data_delete (GtkTreeDragSource *drag_source, - GtkTreePath *path) -{ - GtkTreeDragSourceIface *iface = GTK_TREE_DRAG_SOURCE_GET_IFACE (drag_source); - - g_return_val_if_fail (iface->drag_data_delete != NULL, FALSE); - g_return_val_if_fail (path != NULL, FALSE); - - return (* iface->drag_data_delete) (drag_source, path); -} - -/** - * gtk_tree_drag_source_drag_data_get: - * @drag_source: a `GtkTreeDragSource` - * @path: row that was dragged - * - * Asks the `GtkTreeDragSource` to return a `GdkContentProvider` representing - * the row at @path. Should robustly handle a @path no - * longer found in the model! - * - * Returns: (nullable) (transfer full): a `GdkContentProvider` for the - * given @path - **/ -GdkContentProvider * -gtk_tree_drag_source_drag_data_get (GtkTreeDragSource *drag_source, - GtkTreePath *path) -{ - GtkTreeDragSourceIface *iface = GTK_TREE_DRAG_SOURCE_GET_IFACE (drag_source); - - g_return_val_if_fail (iface->drag_data_get != NULL, FALSE); - g_return_val_if_fail (path != NULL, FALSE); - - return (* iface->drag_data_get) (drag_source, path); -} - -/** - * gtk_tree_drag_dest_drag_data_received: - * @drag_dest: a `GtkTreeDragDest` - * @dest: row to drop in front of - * @value: data to drop - * - * Asks the `GtkTreeDragDest` to insert a row before the path @dest, - * deriving the contents of the row from @value. If @dest is - * outside the tree so that inserting before it is impossible, %FALSE - * will be returned. Also, %FALSE may be returned if the new row is - * not created for some model-specific reason. Should robustly handle - * a @dest no longer found in the model! - * - * Returns: whether a new row was created before position @dest - **/ -gboolean -gtk_tree_drag_dest_drag_data_received (GtkTreeDragDest *drag_dest, - GtkTreePath *dest, - const GValue *value) -{ - GtkTreeDragDestIface *iface = GTK_TREE_DRAG_DEST_GET_IFACE (drag_dest); - - g_return_val_if_fail (iface->drag_data_received != NULL, FALSE); - g_return_val_if_fail (dest != NULL, FALSE); - g_return_val_if_fail (value != NULL, FALSE); - - return (* iface->drag_data_received) (drag_dest, dest, value); -} - - -/** - * gtk_tree_drag_dest_row_drop_possible: - * @drag_dest: a `GtkTreeDragDest` - * @dest_path: destination row - * @value: the data being dropped - * - * Determines whether a drop is possible before the given @dest_path, - * at the same depth as @dest_path. i.e., can we drop the data in - * @value at that location. @dest_path does not have to - * exist; the return value will almost certainly be %FALSE if the - * parent of @dest_path doesn’t exist, though. - * - * Returns: %TRUE if a drop is possible before @dest_path - **/ -gboolean -gtk_tree_drag_dest_row_drop_possible (GtkTreeDragDest *drag_dest, - GtkTreePath *dest_path, - const GValue *value) -{ - GtkTreeDragDestIface *iface = GTK_TREE_DRAG_DEST_GET_IFACE (drag_dest); - - g_return_val_if_fail (iface->row_drop_possible != NULL, FALSE); - g_return_val_if_fail (dest_path != NULL, FALSE); - g_return_val_if_fail (value != NULL, FALSE); - - return (* iface->row_drop_possible) (drag_dest, dest_path, value); -} - -typedef struct _GtkTreeRowData GtkTreeRowData; - -struct _GtkTreeRowData -{ - GtkTreeModel *model; - char path[4]; -}; - -static GtkTreeRowData * -gtk_tree_row_data_copy (GtkTreeRowData *src) -{ - return g_memdup2 (src, sizeof (GtkTreeRowData) + strlen (src->path) + 1 - - (sizeof (GtkTreeRowData) - G_STRUCT_OFFSET (GtkTreeRowData, path))); -} - -G_DEFINE_BOXED_TYPE (GtkTreeRowData, gtk_tree_row_data, - gtk_tree_row_data_copy, - g_free) - -/** - * gtk_tree_create_row_drag_content: - * @tree_model: a `GtkTreeModel` - * @path: a row in @tree_model - * - * Creates a content provider for dragging @path from @tree_model. - * - * Returns: (transfer full): a new `GdkContentProvider` - */ -GdkContentProvider * -gtk_tree_create_row_drag_content (GtkTreeModel *tree_model, - GtkTreePath *path) -{ - GdkContentProvider *content; - GtkTreeRowData *trd; - char *path_str; - int len; - int struct_size; - - g_return_val_if_fail (GTK_IS_TREE_MODEL (tree_model), FALSE); - g_return_val_if_fail (path != NULL, FALSE); - - path_str = gtk_tree_path_to_string (path); - - len = strlen (path_str); - - /* the old allocate-end-of-struct-to-hold-string trick */ - struct_size = sizeof (GtkTreeRowData) + len + 1 - - (sizeof (GtkTreeRowData) - G_STRUCT_OFFSET (GtkTreeRowData, path)); - - trd = g_malloc (struct_size); - - strcpy (trd->path, path_str); - - g_free (path_str); - - trd->model = tree_model; - - content = gdk_content_provider_new_typed (GTK_TYPE_TREE_ROW_DATA, trd); - - g_free (trd); - - return content; -} - -/** - * gtk_tree_get_row_drag_data: - * @value: a `GValue` - * @tree_model: (nullable) (optional) (transfer none) (out): a `GtkTreeModel` - * @path: (nullable) (optional) (out): row in @tree_model - * - * Obtains a @tree_model and @path from value of target type - * %GTK_TYPE_TREE_ROW_DATA. - * - * The returned path must be freed with gtk_tree_path_free(). - * - * Returns: %TRUE if @selection_data had target type %GTK_TYPE_TREE_ROW_DATA - * is otherwise valid - **/ -gboolean -gtk_tree_get_row_drag_data (const GValue *value, - GtkTreeModel **tree_model, - GtkTreePath **path) -{ - GtkTreeRowData *trd; - - g_return_val_if_fail (value != NULL, FALSE); - - if (tree_model) - *tree_model = NULL; - - if (path) - *path = NULL; - - if (!G_VALUE_HOLDS (value, GTK_TYPE_TREE_ROW_DATA)) - return FALSE; - - trd = g_value_get_boxed (value); - if (trd == NULL) - return FALSE; - - if (tree_model) - *tree_model = trd->model; - - if (path) - *path = gtk_tree_path_new_from_string (trd->path); - - return TRUE; -} diff --git a/gtk/gtktreednd.h b/gtk/gtktreednd.h deleted file mode 100644 index 28a28dc6b9..0000000000 --- a/gtk/gtktreednd.h +++ /dev/null @@ -1,168 +0,0 @@ -/* gtktreednd.h - * Copyright (C) 2001 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#ifndef __GTK_TREE_DND_H__ -#define __GTK_TREE_DND_H__ - -#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) -#error "Only can be included directly." -#endif - -#include - -G_BEGIN_DECLS - -/** - * GTK_TYPE_TREE_ROW_DATA: - * Magic `GType` to use when dragging rows in a `GtkTreeModel`. - * - * Data in this format will be provided by gtk_tree_create_row_drag_content() - * and can be consumed via gtk_tree_get_row_drag_data(). - */ -#define GTK_TYPE_TREE_ROW_DATA (gtk_tree_row_data_get_type ()) -GDK_AVAILABLE_IN_ALL -GType gtk_tree_row_data_get_type (void) G_GNUC_CONST; - - -#define GTK_TYPE_TREE_DRAG_SOURCE (gtk_tree_drag_source_get_type ()) -#define GTK_TREE_DRAG_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_TREE_DRAG_SOURCE, GtkTreeDragSource)) -#define GTK_IS_TREE_DRAG_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_TREE_DRAG_SOURCE)) -#define GTK_TREE_DRAG_SOURCE_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GTK_TYPE_TREE_DRAG_SOURCE, GtkTreeDragSourceIface)) - -typedef struct _GtkTreeDragSource GtkTreeDragSource; /* Dummy typedef */ -typedef struct _GtkTreeDragSourceIface GtkTreeDragSourceIface; - -/** - * GtkTreeDragSourceIface: - * @row_draggable: Asks the `GtkTreeDragSource` whether a particular - * row can be used as the source of a DND operation. - * @drag_data_get: Asks the `GtkTreeDragSource` to fill in - * selection_data with a representation of the row at path. - * @drag_data_delete: Asks the `GtkTreeDragSource` to delete the row at - * path, because it was moved somewhere else via drag-and-drop. - */ -struct _GtkTreeDragSourceIface -{ - /*< private >*/ - GTypeInterface g_iface; - - /*< public >*/ - - /* VTable - not signals */ - - gboolean (* row_draggable) (GtkTreeDragSource *drag_source, - GtkTreePath *path); - - GdkContentProvider * (* drag_data_get)(GtkTreeDragSource *drag_source, - GtkTreePath *path); - - gboolean (* drag_data_delete) (GtkTreeDragSource *drag_source, - GtkTreePath *path); -}; - -GDK_AVAILABLE_IN_ALL -GType gtk_tree_drag_source_get_type (void) G_GNUC_CONST; - -/* Returns whether the given row can be dragged */ -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_drag_source_row_draggable (GtkTreeDragSource *drag_source, - GtkTreePath *path); - -/* Deletes the given row, or returns FALSE if it can't */ -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_drag_source_drag_data_delete (GtkTreeDragSource *drag_source, - GtkTreePath *path); - -/* Fills in selection_data with type selection_data->target based on - * the row denoted by path, returns TRUE if it does anything - */ -GDK_AVAILABLE_IN_ALL -GdkContentProvider * - gtk_tree_drag_source_drag_data_get (GtkTreeDragSource *drag_source, - GtkTreePath *path); - -#define GTK_TYPE_TREE_DRAG_DEST (gtk_tree_drag_dest_get_type ()) -#define GTK_TREE_DRAG_DEST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_TREE_DRAG_DEST, GtkTreeDragDest)) -#define GTK_IS_TREE_DRAG_DEST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_TREE_DRAG_DEST)) -#define GTK_TREE_DRAG_DEST_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GTK_TYPE_TREE_DRAG_DEST, GtkTreeDragDestIface)) - -typedef struct _GtkTreeDragDest GtkTreeDragDest; /* Dummy typedef */ -typedef struct _GtkTreeDragDestIface GtkTreeDragDestIface; - -/** - * GtkTreeDragDestIface: - * @drag_data_received: Asks the `GtkTreeDragDest` to insert a row - * before the path dest, deriving the contents of the row from - * selection_data. - * @row_drop_possible: Determines whether a drop is possible before - * the given dest_path, at the same depth as dest_path. - */ -struct _GtkTreeDragDestIface -{ - /*< private >*/ - GTypeInterface g_iface; - - /*< public >*/ - - /* VTable - not signals */ - - gboolean (* drag_data_received) (GtkTreeDragDest *drag_dest, - GtkTreePath *dest, - const GValue *value); - - gboolean (* row_drop_possible) (GtkTreeDragDest *drag_dest, - GtkTreePath *dest_path, - const GValue *value); -}; - -GDK_AVAILABLE_IN_ALL -GType gtk_tree_drag_dest_get_type (void) G_GNUC_CONST; - -/* Inserts a row before dest which contains data in selection_data, - * or returns FALSE if it can't - */ -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_drag_dest_drag_data_received (GtkTreeDragDest *drag_dest, - GtkTreePath *dest, - const GValue *value); - - -/* Returns TRUE if we can drop before path; path may not exist. */ -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_drag_dest_row_drop_possible (GtkTreeDragDest *drag_dest, - GtkTreePath *dest_path, - const GValue *value); - - -/* The selection data would normally have target type GTK_TREE_MODEL_ROW in this - * case. If the target is wrong these functions return FALSE. - */ -GDK_AVAILABLE_IN_ALL -GdkContentProvider * - gtk_tree_create_row_drag_content (GtkTreeModel *tree_model, - GtkTreePath *path); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_get_row_drag_data (const GValue *value, - GtkTreeModel **tree_model, - GtkTreePath **path); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkTreeDragDest, g_object_unref) -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkTreeDragSource, g_object_unref) - -G_END_DECLS - -#endif /* __GTK_TREE_DND_H__ */ diff --git a/gtk/gtktreemodel.c b/gtk/gtktreemodel.c deleted file mode 100644 index 7034bd0dd4..0000000000 --- a/gtk/gtktreemodel.c +++ /dev/null @@ -1,2589 +0,0 @@ -/* gtktreemodel.c - * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#include "config.h" -#include -#include -#include -#include -#include -#include "gtktreemodel.h" -#include "gtktreeview.h" -#include "gtktreeprivate.h" -#include "gtkmarshalers.h" -#include "gtkprivate.h" - -/** - * GtkTreeModel: - * - * The tree interface used by GtkTreeView - * - * The `GtkTreeModel` interface defines a generic tree interface for - * use by the `GtkTreeView` widget. It is an abstract interface, and - * is designed to be usable with any appropriate data structure. The - * programmer just has to implement this interface on their own data - * type for it to be viewable by a `GtkTreeView` widget. - * - * The model is represented as a hierarchical tree of strongly-typed, - * columned data. In other words, the model can be seen as a tree where - * every node has different values depending on which column is being - * queried. The type of data found in a column is determined by using - * the GType system (ie. %G_TYPE_INT, %GTK_TYPE_BUTTON, %G_TYPE_POINTER, - * etc). The types are homogeneous per column across all nodes. It is - * important to note that this interface only provides a way of examining - * a model and observing changes. The implementation of each individual - * model decides how and if changes are made. - * - * In order to make life simpler for programmers who do not need to - * write their own specialized model, two generic models are provided - * — the `GtkTreeStore` and the `GtkListStore`. To use these, the - * developer simply pushes data into these models as necessary. These - * models provide the data structure as well as all appropriate tree - * interfaces. As a result, implementing drag and drop, sorting, and - * storing data is trivial. For the vast majority of trees and lists, - * these two models are sufficient. - * - * Models are accessed on a node/column level of granularity. One can - * query for the value of a model at a certain node and a certain - * column on that node. There are two structures used to reference a - * particular node in a model. They are the [struct@Gtk.TreePath] and - * the [struct@Gtk.TreeIter] (“iter” is short for iterator). Most of the - * interface consists of operations on a [struct@Gtk.TreeIter]. - * - * A path is essentially a potential node. It is a location on a model - * that may or may not actually correspond to a node on a specific - * model. A [struct@Gtk.TreePath] can be converted into either an - * array of unsigned integers or a string. The string form is a list - * of numbers separated by a colon. Each number refers to the offset - * at that level. Thus, the path `0` refers to the root - * node and the path `2:4` refers to the fifth child of - * the third node. - * - * By contrast, a [struct@Gtk.TreeIter] is a reference to a specific node on - * a specific model. It is a generic struct with an integer and three - * generic pointers. These are filled in by the model in a model-specific - * way. One can convert a path to an iterator by calling - * gtk_tree_model_get_iter(). These iterators are the primary way - * of accessing a model and are similar to the iterators used by - * `GtkTextBuffer`. They are generally statically allocated on the - * stack and only used for a short time. The model interface defines - * a set of operations using them for navigating the model. - * - * It is expected that models fill in the iterator with private data. - * For example, the `GtkListStore` model, which is internally a simple - * linked list, stores a list node in one of the pointers. The - * `GtkTreeModel`Sort stores an array and an offset in two of the - * pointers. Additionally, there is an integer field. This field is - * generally filled with a unique stamp per model. This stamp is for - * catching errors resulting from using invalid iterators with a model. - * - * The lifecycle of an iterator can be a little confusing at first. - * Iterators are expected to always be valid for as long as the model - * is unchanged (and doesn’t emit a signal). The model is considered - * to own all outstanding iterators and nothing needs to be done to - * free them from the user’s point of view. Additionally, some models - * guarantee that an iterator is valid for as long as the node it refers - * to is valid (most notably the `GtkTreeStore` and `GtkListStore`). - * Although generally uninteresting, as one always has to allow for - * the case where iterators do not persist beyond a signal, some very - * important performance enhancements were made in the sort model. - * As a result, the %GTK_TREE_MODEL_ITERS_PERSIST flag was added to - * indicate this behavior. - * - * To help show some common operation of a model, some examples are - * provided. The first example shows three ways of getting the iter at - * the location `3:2:5`. While the first method shown is - * easier, the second is much more common, as you often get paths from - * callbacks. - * - * ## Acquiring a `GtkTreeIter` - * - * ```c - * // Three ways of getting the iter pointing to the location - * GtkTreePath *path; - * GtkTreeIter iter; - * GtkTreeIter parent_iter; - * - * // get the iterator from a string - * gtk_tree_model_get_iter_from_string (model, - * &iter, - * "3:2:5"); - * - * // get the iterator from a path - * path = gtk_tree_path_new_from_string ("3:2:5"); - * gtk_tree_model_get_iter (model, &iter, path); - * gtk_tree_path_free (path); - * - * // walk the tree to find the iterator - * gtk_tree_model_iter_nth_child (model, &iter, - * NULL, 3); - * parent_iter = iter; - * gtk_tree_model_iter_nth_child (model, &iter, - * &parent_iter, 2); - * parent_iter = iter; - * gtk_tree_model_iter_nth_child (model, &iter, - * &parent_iter, 5); - * ``` - * - * This second example shows a quick way of iterating through a list - * and getting a string and an integer from each row. The - * populate_model() function used below is not - * shown, as it is specific to the `GtkListStore`. For information on - * how to write such a function, see the `GtkListStore` documentation. - * - * ## Reading data from a `GtkTreeModel` - * - * ```c - * enum - * { - * STRING_COLUMN, - * INT_COLUMN, - * N_COLUMNS - * }; - * - * ... - * - * GtkTreeModel *list_store; - * GtkTreeIter iter; - * gboolean valid; - * int row_count = 0; - * - * // make a new list_store - * list_store = gtk_list_store_new (N_COLUMNS, - * G_TYPE_STRING, - * G_TYPE_INT); - * - * // Fill the list store with data - * populate_model (list_store); - * - * // Get the first iter in the list, check it is valid and walk - * // through the list, reading each row. - * - * valid = gtk_tree_model_get_iter_first (list_store, - * &iter); - * while (valid) - * { - * char *str_data; - * int int_data; - * - * // Make sure you terminate calls to gtk_tree_model_get() with a “-1” value - * gtk_tree_model_get (list_store, &iter, - * STRING_COLUMN, &str_data, - * INT_COLUMN, &int_data, - * -1); - * - * // Do something with the data - * g_print ("Row %d: (%s,%d)\n", - * row_count, str_data, int_data); - * g_free (str_data); - * - * valid = gtk_tree_model_iter_next (list_store, - * &iter); - * row_count++; - * } - * ``` - * - * The `GtkTreeModel` interface contains two methods for reference - * counting: gtk_tree_model_ref_node() and gtk_tree_model_unref_node(). - * These two methods are optional to implement. The reference counting - * is meant as a way for views to let models know when nodes are being - * displayed. `GtkTreeView` will take a reference on a node when it is - * visible, which means the node is either in the toplevel or expanded. - * Being displayed does not mean that the node is currently directly - * visible to the user in the viewport. Based on this reference counting - * scheme a caching model, for example, can decide whether or not to cache - * a node based on the reference count. A file-system based model would - * not want to keep the entire file hierarchy in memory, but just the - * folders that are currently expanded in every current view. - * - * When working with reference counting, the following rules must be taken - * into account: - * - * - Never take a reference on a node without owning a reference on its parent. - * This means that all parent nodes of a referenced node must be referenced - * as well. - * - * - Outstanding references on a deleted node are not released. This is not - * possible because the node has already been deleted by the time the - * row-deleted signal is received. - * - * - Models are not obligated to emit a signal on rows of which none of its - * siblings are referenced. To phrase this differently, signals are only - * required for levels in which nodes are referenced. For the root level - * however, signals must be emitted at all times (however the root level - * is always referenced when any view is attached). - */ - -#define INITIALIZE_TREE_ITER(Iter) \ - G_STMT_START{ \ - (Iter)->stamp = 0; \ - (Iter)->user_data = NULL; \ - (Iter)->user_data2 = NULL; \ - (Iter)->user_data3 = NULL; \ - }G_STMT_END - -#define ROW_REF_DATA_STRING "gtk-tree-row-refs" - -enum { - ROW_CHANGED, - ROW_INSERTED, - ROW_HAS_CHILD_TOGGLED, - ROW_DELETED, - ROWS_REORDERED, - LAST_SIGNAL -}; - -static guint tree_model_signals[LAST_SIGNAL] = { 0 }; - -/** - * GtkTreePath: - * - * An opaque structure representing a path to a row in a model. - */ -struct _GtkTreePath -{ - int depth; /* Number of elements */ - int alloc; /* Number of allocated elements */ - int *indices; -}; - -typedef struct -{ - GSList *list; -} RowRefList; - -static void gtk_tree_model_base_init (gpointer g_class); - -/* custom closures */ -static void row_inserted_marshal (GClosure *closure, - GValue /* out */ *return_value, - guint n_param_value, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data); -static void row_deleted_marshal (GClosure *closure, - GValue /* out */ *return_value, - guint n_param_value, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data); -static void rows_reordered_marshal (GClosure *closure, - GValue /* out */ *return_value, - guint n_param_value, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data); - -static void gtk_tree_row_ref_inserted (RowRefList *refs, - GtkTreePath *path, - GtkTreeIter *iter); -static void gtk_tree_row_ref_deleted (RowRefList *refs, - GtkTreePath *path); -static void gtk_tree_row_ref_reordered (RowRefList *refs, - GtkTreePath *path, - GtkTreeIter *iter, - int *new_order); - -GType -gtk_tree_model_get_type (void) -{ - static GType tree_model_type = 0; - - if (! tree_model_type) - { - const GTypeInfo tree_model_info = - { - sizeof (GtkTreeModelIface), /* class_size */ - gtk_tree_model_base_init, /* base_init */ - NULL, /* base_finalize */ - NULL, - NULL, /* class_finalize */ - NULL, /* class_data */ - 0, - 0, /* n_preallocs */ - NULL - }; - - tree_model_type = - g_type_register_static (G_TYPE_INTERFACE, I_("GtkTreeModel"), - &tree_model_info, 0); - - g_type_interface_add_prerequisite (tree_model_type, G_TYPE_OBJECT); - } - - return tree_model_type; -} - -static void -gtk_tree_model_base_init (gpointer g_class) -{ - static gboolean initialized = FALSE; - GClosure *closure; - - if (! initialized) - { - GType row_inserted_params[2]; - GType row_deleted_params[1]; - GType rows_reordered_params[3]; - - row_inserted_params[0] = GTK_TYPE_TREE_PATH | G_SIGNAL_TYPE_STATIC_SCOPE; - row_inserted_params[1] = GTK_TYPE_TREE_ITER; - - row_deleted_params[0] = GTK_TYPE_TREE_PATH | G_SIGNAL_TYPE_STATIC_SCOPE; - - rows_reordered_params[0] = GTK_TYPE_TREE_PATH | G_SIGNAL_TYPE_STATIC_SCOPE; - rows_reordered_params[1] = GTK_TYPE_TREE_ITER; - rows_reordered_params[2] = G_TYPE_POINTER; - - /** - * GtkTreeModel::row-changed: - * @tree_model: the `GtkTreeModel` on which the signal is emitted - * @path: a `GtkTreePath` identifying the changed row - * @iter: a valid `GtkTreeIter` pointing to the changed row - * - * This signal is emitted when a row in the model has changed. - */ - tree_model_signals[ROW_CHANGED] = - g_signal_new (I_("row-changed"), - GTK_TYPE_TREE_MODEL, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkTreeModelIface, row_changed), - NULL, NULL, - _gtk_marshal_VOID__BOXED_BOXED, - G_TYPE_NONE, 2, - GTK_TYPE_TREE_PATH | G_SIGNAL_TYPE_STATIC_SCOPE, - GTK_TYPE_TREE_ITER); - g_signal_set_va_marshaller (tree_model_signals[ROW_CHANGED], - G_TYPE_FROM_CLASS (g_class), - _gtk_marshal_VOID__BOXED_BOXEDv); - - /* We need to get notification about structure changes - * to update row references., so instead of using the - * standard g_signal_new() with an offset into our interface - * structure, we use a customs closures for the class - * closures (default handlers) that first update row references - * and then calls the function from the interface structure. - * - * The reason we don't simply update the row references from - * the wrapper functions (gtk_tree_model_row_inserted(), etc.) - * is to keep proper ordering with respect to signal handlers - * connected normally and after. - */ - - /** - * GtkTreeModel::row-inserted: - * @tree_model: the `GtkTreeModel` on which the signal is emitted - * @path: a `GtkTreePath` identifying the new row - * @iter: a valid `GtkTreeIter` pointing to the new row - * - * This signal is emitted when a new row has been inserted in - * the model. - * - * Note that the row may still be empty at this point, since - * it is a common pattern to first insert an empty row, and - * then fill it with the desired values. - */ - closure = g_closure_new_simple (sizeof (GClosure), NULL); - g_closure_set_marshal (closure, row_inserted_marshal); - tree_model_signals[ROW_INSERTED] = - g_signal_newv (I_("row-inserted"), - GTK_TYPE_TREE_MODEL, - G_SIGNAL_RUN_FIRST, - closure, - NULL, NULL, - _gtk_marshal_VOID__BOXED_BOXED, - G_TYPE_NONE, 2, - row_inserted_params); - g_signal_set_va_marshaller (tree_model_signals[ROW_INSERTED], - G_TYPE_FROM_CLASS (g_class), - _gtk_marshal_VOID__BOXED_BOXEDv); - - /** - * GtkTreeModel::row-has-child-toggled: - * @tree_model: the `GtkTreeModel` on which the signal is emitted - * @path: a `GtkTreePath` identifying the row - * @iter: a valid `GtkTreeIter` pointing to the row - * - * This signal is emitted when a row has gotten the first child - * row or lost its last child row. - */ - tree_model_signals[ROW_HAS_CHILD_TOGGLED] = - g_signal_new (I_("row-has-child-toggled"), - GTK_TYPE_TREE_MODEL, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkTreeModelIface, row_has_child_toggled), - NULL, NULL, - _gtk_marshal_VOID__BOXED_BOXED, - G_TYPE_NONE, 2, - GTK_TYPE_TREE_PATH | G_SIGNAL_TYPE_STATIC_SCOPE, - GTK_TYPE_TREE_ITER); - g_signal_set_va_marshaller (tree_model_signals[ROW_HAS_CHILD_TOGGLED], - G_TYPE_FROM_CLASS (g_class), - _gtk_marshal_VOID__BOXED_BOXEDv); - - /** - * GtkTreeModel::row-deleted: - * @tree_model: the `GtkTreeModel` on which the signal is emitted - * @path: a `GtkTreePath` identifying the row - * - * This signal is emitted when a row has been deleted. - * - * Note that no iterator is passed to the signal handler, - * since the row is already deleted. - * - * This should be called by models after a row has been removed. - * The location pointed to by @path should be the location that - * the row previously was at. It may not be a valid location anymore. - */ - closure = g_closure_new_simple (sizeof (GClosure), NULL); - g_closure_set_marshal (closure, row_deleted_marshal); - tree_model_signals[ROW_DELETED] = - g_signal_newv (I_("row-deleted"), - GTK_TYPE_TREE_MODEL, - G_SIGNAL_RUN_FIRST, - closure, - NULL, NULL, - NULL, - G_TYPE_NONE, 1, - row_deleted_params); - - /** - * GtkTreeModel::rows-reordered: (skip) - * @tree_model: the `GtkTreeModel` on which the signal is emitted - * @path: a `GtkTreePath` identifying the tree node whose children - * have been reordered - * @iter: a valid `GtkTreeIter` pointing to the node whose children - * have been reordered, or %NULL if the depth of @path is 0 - * @new_order: an array of integers mapping the current position - * of each child to its old position before the re-ordering, - * i.e. @new_order`[newpos] = oldpos` - * - * This signal is emitted when the children of a node in the - * `GtkTreeModel` have been reordered. - * - * Note that this signal is not emitted - * when rows are reordered by DND, since this is implemented - * by removing and then reinserting the row. - */ - closure = g_closure_new_simple (sizeof (GClosure), NULL); - g_closure_set_marshal (closure, rows_reordered_marshal); - tree_model_signals[ROWS_REORDERED] = - g_signal_newv (I_("rows-reordered"), - GTK_TYPE_TREE_MODEL, - G_SIGNAL_RUN_FIRST, - closure, - NULL, NULL, - _gtk_marshal_VOID__BOXED_BOXED_POINTER, - G_TYPE_NONE, 3, - rows_reordered_params); - g_signal_set_va_marshaller (tree_model_signals[ROWS_REORDERED], - G_TYPE_FROM_CLASS (g_class), - _gtk_marshal_VOID__BOXED_BOXED_POINTERv); - initialized = TRUE; - } -} - -static void -row_inserted_marshal (GClosure *closure, - GValue /* out */ *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data) -{ - GtkTreeModelIface *iface; - - void (* row_inserted_callback) (GtkTreeModel *tree_model, - GtkTreePath *path, - GtkTreeIter *iter) = NULL; - - GObject *model = g_value_get_object (param_values + 0); - GtkTreePath *path = (GtkTreePath *)g_value_get_boxed (param_values + 1); - GtkTreeIter *iter = (GtkTreeIter *)g_value_get_boxed (param_values + 2); - - /* first, we need to update internal row references */ - gtk_tree_row_ref_inserted ((RowRefList *)g_object_get_data (model, ROW_REF_DATA_STRING), - path, iter); - - /* fetch the interface ->row_inserted implementation */ - iface = GTK_TREE_MODEL_GET_IFACE (model); - row_inserted_callback = G_STRUCT_MEMBER (gpointer, iface, - G_STRUCT_OFFSET (GtkTreeModelIface, - row_inserted)); - - /* Call that default signal handler, it if has been set */ - if (row_inserted_callback) - row_inserted_callback (GTK_TREE_MODEL (model), path, iter); -} - -static void -row_deleted_marshal (GClosure *closure, - GValue /* out */ *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data) -{ - GtkTreeModelIface *iface; - void (* row_deleted_callback) (GtkTreeModel *tree_model, - GtkTreePath *path) = NULL; - GObject *model = g_value_get_object (param_values + 0); - GtkTreePath *path = (GtkTreePath *)g_value_get_boxed (param_values + 1); - - /* first, we need to update internal row references */ - gtk_tree_row_ref_deleted ((RowRefList *)g_object_get_data (model, ROW_REF_DATA_STRING), - path); - - /* fetch the interface ->row_deleted implementation */ - iface = GTK_TREE_MODEL_GET_IFACE (model); - row_deleted_callback = G_STRUCT_MEMBER (gpointer, iface, - G_STRUCT_OFFSET (GtkTreeModelIface, - row_deleted)); - - /* Call that default signal handler, it if has been set */ - if (row_deleted_callback) - row_deleted_callback (GTK_TREE_MODEL (model), path); -} - -static void -rows_reordered_marshal (GClosure *closure, - GValue /* out */ *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data) -{ - GtkTreeModelIface *iface; - void (* rows_reordered_callback) (GtkTreeModel *tree_model, - GtkTreePath *path, - GtkTreeIter *iter, - int *new_order); - - GObject *model = g_value_get_object (param_values + 0); - GtkTreePath *path = (GtkTreePath *)g_value_get_boxed (param_values + 1); - GtkTreeIter *iter = (GtkTreeIter *)g_value_get_boxed (param_values + 2); - int *new_order = (int *)g_value_get_pointer (param_values + 3); - - /* first, we need to update internal row references */ - gtk_tree_row_ref_reordered ((RowRefList *)g_object_get_data (model, ROW_REF_DATA_STRING), - path, iter, new_order); - - /* fetch the interface ->rows_reordered implementation */ - iface = GTK_TREE_MODEL_GET_IFACE (model); - rows_reordered_callback = G_STRUCT_MEMBER (gpointer, iface, - G_STRUCT_OFFSET (GtkTreeModelIface, - rows_reordered)); - - /* Call that default signal handler, it if has been set */ - if (rows_reordered_callback) - rows_reordered_callback (GTK_TREE_MODEL (model), path, iter, new_order); -} - -/** - * gtk_tree_path_new: - * - * Creates a new `GtkTreePath` - * This refers to a row. - * - * Returns: A newly created `GtkTreePath`. - */ -GtkTreePath * -gtk_tree_path_new (void) -{ - GtkTreePath *retval; - retval = g_slice_new (GtkTreePath); - retval->depth = 0; - retval->alloc = 0; - retval->indices = NULL; - - return retval; -} - -/** - * gtk_tree_path_new_from_string: - * @path: The string representation of a path - * - * Creates a new `GtkTreePath` initialized to @path. - * - * @path is expected to be a colon separated list of numbers. - * For example, the string “10:4:0” would create a path of depth - * 3 pointing to the 11th child of the root node, the 5th - * child of that 11th child, and the 1st child of that 5th child. - * If an invalid path string is passed in, %NULL is returned. - * - * Returns: (nullable): A newly-created `GtkTreePath` - */ -GtkTreePath * -gtk_tree_path_new_from_string (const char *path) -{ - GtkTreePath *retval; - const char *orig_path = path; - char *ptr; - int i; - - g_return_val_if_fail (path != NULL, NULL); - g_return_val_if_fail (*path != '\000', NULL); - - retval = gtk_tree_path_new (); - - while (1) - { - i = strtol (path, &ptr, 10); - if (i < 0) - { - g_warning (G_STRLOC ": Negative numbers in path %s passed to gtk_tree_path_new_from_string", orig_path); - gtk_tree_path_free (retval); - return NULL; - } - - gtk_tree_path_append_index (retval, i); - - if (*ptr == '\000') - break; - if (ptr == path || *ptr != ':') - { - g_warning (G_STRLOC ": Invalid path %s passed to gtk_tree_path_new_from_string", orig_path); - gtk_tree_path_free (retval); - return NULL; - } - path = ptr + 1; - } - - return retval; -} - -/** - * gtk_tree_path_new_from_indices: - * @first_index: first integer - * @...: list of integers terminated by -1 - * - * Creates a new path with @first_index and @varargs as indices. - * - * Returns: A newly created `GtkTreePath` - */ -GtkTreePath * -gtk_tree_path_new_from_indices (int first_index, - ...) -{ - int arg; - va_list args; - GtkTreePath *path; - - path = gtk_tree_path_new (); - - va_start (args, first_index); - arg = first_index; - - while (arg != -1) - { - gtk_tree_path_append_index (path, arg); - arg = va_arg (args, int); - } - - va_end (args); - - return path; -} - -/** - * gtk_tree_path_new_from_indicesv: (rename-to gtk_tree_path_new_from_indices) - * @indices: (array length=length): array of indices - * @length: length of @indices array - * - * Creates a new path with the given @indices array of @length. - * - * Returns: A newly created `GtkTreePath` - */ -GtkTreePath * -gtk_tree_path_new_from_indicesv (int *indices, - gsize length) -{ - GtkTreePath *path; - - g_return_val_if_fail (indices != NULL && length != 0, NULL); - - path = gtk_tree_path_new (); - path->alloc = length; - path->depth = length; - path->indices = g_new (int, length); - memcpy (path->indices, indices, length * sizeof (int)); - - return path; -} - -/** - * gtk_tree_path_to_string: - * @path: a `GtkTreePath` - * - * Generates a string representation of the path. - * - * This string is a “:” separated list of numbers. - * For example, “4:10:0:3” would be an acceptable - * return value for this string. If the path has - * depth 0, %NULL is returned. - * - * Returns: (nullable): A newly-allocated string - */ -char * -gtk_tree_path_to_string (GtkTreePath *path) -{ - char *retval, *ptr, *end; - int i, n; - - g_return_val_if_fail (path != NULL, NULL); - - if (path->depth == 0) - return NULL; - - n = path->depth * 12; - ptr = retval = g_new0 (char, n); - end = ptr + n; - g_snprintf (retval, end - ptr, "%d", path->indices[0]); - while (*ptr != '\000') - ptr++; - - for (i = 1; i < path->depth; i++) - { - g_snprintf (ptr, end - ptr, ":%d", path->indices[i]); - while (*ptr != '\000') - ptr++; - } - - return retval; -} - -/** - * gtk_tree_path_new_first: - * - * Creates a new `GtkTreePath`. - * - * The string representation of this path is “0”. - * - * Returns: A new `GtkTreePath` - */ -GtkTreePath * -gtk_tree_path_new_first (void) -{ - GtkTreePath *retval; - - retval = gtk_tree_path_new (); - gtk_tree_path_append_index (retval, 0); - - return retval; -} - -/** - * gtk_tree_path_append_index: - * @path: a `GtkTreePath` - * @index_: the index - * - * Appends a new index to a path. - * - * As a result, the depth of the path is increased. - */ -void -gtk_tree_path_append_index (GtkTreePath *path, - int index_) -{ - g_return_if_fail (path != NULL); - g_return_if_fail (index_ >= 0); - - if (path->depth == path->alloc) - { - path->alloc = MAX (path->alloc * 2, 1); - path->indices = g_renew (int, path->indices, path->alloc); - } - - path->depth += 1; - path->indices[path->depth - 1] = index_; -} - -/** - * gtk_tree_path_prepend_index: - * @path: a `GtkTreePath` - * @index_: the index - * - * Prepends a new index to a path. - * - * As a result, the depth of the path is increased. - */ -void -gtk_tree_path_prepend_index (GtkTreePath *path, - int index) -{ - if (path->depth == path->alloc) - { - int *indices; - path->alloc = MAX (path->alloc * 2, 1); - indices = g_new (int, path->alloc); - memcpy (indices + 1, path->indices, path->depth * sizeof (int)); - g_free (path->indices); - path->indices = indices; - } - else if (path->depth > 0) - memmove (path->indices + 1, path->indices, path->depth * sizeof (int)); - - path->depth += 1; - path->indices[0] = index; -} - -/** - * gtk_tree_path_get_depth: - * @path: a `GtkTreePath` - * - * Returns the current depth of @path. - * - * Returns: The depth of @path - */ -int -gtk_tree_path_get_depth (GtkTreePath *path) -{ - g_return_val_if_fail (path != NULL, 0); - - return path->depth; -} - -/** - * gtk_tree_path_get_indices: (skip) - * @path: a `GtkTreePath` - * - * Returns the current indices of @path. - * - * This is an array of integers, each representing a node in a tree. - * This value should not be freed. - * - * The length of the array can be obtained with gtk_tree_path_get_depth(). - * - * Returns: (nullable) (transfer none): The current indices - */ -int * -gtk_tree_path_get_indices (GtkTreePath *path) -{ - g_return_val_if_fail (path != NULL, NULL); - - return path->indices; -} - -/** - * gtk_tree_path_get_indices_with_depth: (rename-to gtk_tree_path_get_indices) - * @path: a `GtkTreePath` - * @depth: (out) (optional): return location for number of elements - * returned in the integer array - * - * Returns the current indices of @path. - * - * This is an array of integers, each representing a node in a tree. - * It also returns the number of elements in the array. - * The array should not be freed. - * - * Returns: (array length=depth) (transfer none) (nullable): The current - * indices - */ -int * -gtk_tree_path_get_indices_with_depth (GtkTreePath *path, - int *depth) -{ - g_return_val_if_fail (path != NULL, NULL); - - if (depth) - *depth = path->depth; - - return path->indices; -} - -/** - * gtk_tree_path_free: - * @path: (nullable): a `GtkTreePath` - * - * Frees @path. If @path is %NULL, it simply returns. - */ -void -gtk_tree_path_free (GtkTreePath *path) -{ - if (!path) - return; - - g_free (path->indices); - g_slice_free (GtkTreePath, path); -} - -/** - * gtk_tree_path_copy: - * @path: a `GtkTreePath` - * - * Creates a new `GtkTreePath` as a copy of @path. - * - * Returns: a new `GtkTreePath` - */ -GtkTreePath * -gtk_tree_path_copy (const GtkTreePath *path) -{ - GtkTreePath *retval; - - g_return_val_if_fail (path != NULL, NULL); - - retval = g_slice_new (GtkTreePath); - retval->depth = path->depth; - retval->alloc = retval->depth; - retval->indices = g_new (int, path->alloc); - memcpy (retval->indices, path->indices, path->depth * sizeof (int)); - return retval; -} - -G_DEFINE_BOXED_TYPE (GtkTreePath, gtk_tree_path, - gtk_tree_path_copy, - gtk_tree_path_free) - -/** - * gtk_tree_path_compare: - * @a: a `GtkTreePath` - * @b: a `GtkTreePath` to compare with - * - * Compares two paths. - * - * If @a appears before @b in a tree, then -1 is returned. - * If @b appears before @a, then 1 is returned. - * If the two nodes are equal, then 0 is returned. - * - * Returns: the relative positions of @a and @b - */ -int -gtk_tree_path_compare (const GtkTreePath *a, - const GtkTreePath *b) -{ - int p = 0, q = 0; - - g_return_val_if_fail (a != NULL, 0); - g_return_val_if_fail (b != NULL, 0); - g_return_val_if_fail (a->depth > 0, 0); - g_return_val_if_fail (b->depth > 0, 0); - - do - { - if (a->indices[p] == b->indices[q]) - continue; - return (a->indices[p] < b->indices[q]?-1:1); - } - while (++p < a->depth && ++q < b->depth); - if (a->depth == b->depth) - return 0; - return (a->depth < b->depth?-1:1); -} - -/** - * gtk_tree_path_is_ancestor: - * @path: a `GtkTreePath` - * @descendant: another `GtkTreePath` - * - * Returns %TRUE if @descendant is a descendant of @path. - * - * Returns: %TRUE if @descendant is contained inside @path - */ -gboolean -gtk_tree_path_is_ancestor (GtkTreePath *path, - GtkTreePath *descendant) -{ - int i; - - g_return_val_if_fail (path != NULL, FALSE); - g_return_val_if_fail (descendant != NULL, FALSE); - - /* can't be an ancestor if we're deeper */ - if (path->depth >= descendant->depth) - return FALSE; - - i = 0; - while (i < path->depth) - { - if (path->indices[i] != descendant->indices[i]) - return FALSE; - ++i; - } - - return TRUE; -} - -/** - * gtk_tree_path_is_descendant: - * @path: a `GtkTreePath` - * @ancestor: another `GtkTreePath` - * - * Returns %TRUE if @path is a descendant of @ancestor. - * - * Returns: %TRUE if @ancestor contains @path somewhere below it - */ -gboolean -gtk_tree_path_is_descendant (GtkTreePath *path, - GtkTreePath *ancestor) -{ - int i; - - g_return_val_if_fail (path != NULL, FALSE); - g_return_val_if_fail (ancestor != NULL, FALSE); - - /* can't be a descendant if we're shallower in the tree */ - if (path->depth <= ancestor->depth) - return FALSE; - - i = 0; - while (i < ancestor->depth) - { - if (path->indices[i] != ancestor->indices[i]) - return FALSE; - ++i; - } - - return TRUE; -} - - -/** - * gtk_tree_path_next: - * @path: a `GtkTreePath` - * - * Moves the @path to point to the next node at the current depth. - */ -void -gtk_tree_path_next (GtkTreePath *path) -{ - g_return_if_fail (path != NULL); - g_return_if_fail (path->depth > 0); - - path->indices[path->depth - 1] ++; -} - -/** - * gtk_tree_path_prev: - * @path: a `GtkTreePath` - * - * Moves the @path to point to the previous node at the - * current depth, if it exists. - * - * Returns: %TRUE if @path has a previous node, and - * the move was made - */ -gboolean -gtk_tree_path_prev (GtkTreePath *path) -{ - g_return_val_if_fail (path != NULL, FALSE); - - if (path->depth == 0) - return FALSE; - - if (path->indices[path->depth - 1] == 0) - return FALSE; - - path->indices[path->depth - 1] -= 1; - - return TRUE; -} - -/** - * gtk_tree_path_up: - * @path: a `GtkTreePath` - * - * Moves the @path to point to its parent node, if it has a parent. - * - * Returns: %TRUE if @path has a parent, and the move was made - */ -gboolean -gtk_tree_path_up (GtkTreePath *path) -{ - g_return_val_if_fail (path != NULL, FALSE); - - if (path->depth == 0) - return FALSE; - - path->depth--; - - return TRUE; -} - -/** - * gtk_tree_path_down: - * @path: a `GtkTreePath` - * - * Moves @path to point to the first child of the current path. - */ -void -gtk_tree_path_down (GtkTreePath *path) -{ - g_return_if_fail (path != NULL); - - gtk_tree_path_append_index (path, 0); -} - -/** - * gtk_tree_iter_copy: - * @iter: a `GtkTreeIter` - * - * Creates a dynamically allocated tree iterator as a copy of @iter. - * - * This function is not intended for use in applications, - * because you can just copy the structs by value - * (`GtkTreeIter new_iter = iter;`). - * You must free this iter with gtk_tree_iter_free(). - * - * Returns: a newly-allocated copy of @iter - */ -GtkTreeIter * -gtk_tree_iter_copy (GtkTreeIter *iter) -{ - GtkTreeIter *retval; - - g_return_val_if_fail (iter != NULL, NULL); - - retval = g_slice_new (GtkTreeIter); - *retval = *iter; - - return retval; -} - -/** - * gtk_tree_iter_free: - * @iter: a dynamically allocated tree iterator - * - * Frees an iterator that has been allocated by gtk_tree_iter_copy(). - * - * This function is mainly used for language bindings. - */ -void -gtk_tree_iter_free (GtkTreeIter *iter) -{ - g_return_if_fail (iter != NULL); - - g_slice_free (GtkTreeIter, iter); -} - -G_DEFINE_BOXED_TYPE (GtkTreeIter, gtk_tree_iter, - gtk_tree_iter_copy, - gtk_tree_iter_free) - -/** - * gtk_tree_model_get_flags: - * @tree_model: a `GtkTreeModel` - * - * Returns a set of flags supported by this interface. - * - * The flags are a bitwise combination of `GtkTreeModel`Flags. - * The flags supported should not change during the lifetime - * of the @tree_model. - * - * Returns: the flags supported by this interface - */ -GtkTreeModelFlags -gtk_tree_model_get_flags (GtkTreeModel *tree_model) -{ - GtkTreeModelIface *iface; - - g_return_val_if_fail (GTK_IS_TREE_MODEL (tree_model), 0); - - iface = GTK_TREE_MODEL_GET_IFACE (tree_model); - if (iface->get_flags) - return (* iface->get_flags) (tree_model); - - return 0; -} - -/** - * gtk_tree_model_get_n_columns: - * @tree_model: a `GtkTreeModel` - * - * Returns the number of columns supported by @tree_model. - * - * Returns: the number of columns - */ -int -gtk_tree_model_get_n_columns (GtkTreeModel *tree_model) -{ - GtkTreeModelIface *iface; - g_return_val_if_fail (GTK_IS_TREE_MODEL (tree_model), 0); - - iface = GTK_TREE_MODEL_GET_IFACE (tree_model); - g_return_val_if_fail (iface->get_n_columns != NULL, 0); - - return (* iface->get_n_columns) (tree_model); -} - -/** - * gtk_tree_model_get_column_type: - * @tree_model: a `GtkTreeModel` - * @index_: the column index - * - * Returns the type of the column. - * - * Returns: the type of the column - */ -GType -gtk_tree_model_get_column_type (GtkTreeModel *tree_model, - int index) -{ - GtkTreeModelIface *iface; - - g_return_val_if_fail (GTK_IS_TREE_MODEL (tree_model), G_TYPE_INVALID); - - iface = GTK_TREE_MODEL_GET_IFACE (tree_model); - g_return_val_if_fail (iface->get_column_type != NULL, G_TYPE_INVALID); - g_return_val_if_fail (index >= 0, G_TYPE_INVALID); - - return (* iface->get_column_type) (tree_model, index); -} - -/** - * gtk_tree_model_get_iter: - * @tree_model: a `GtkTreeModel` - * @iter: (out): the uninitialized `GtkTreeIter` - * @path: the `GtkTreePath` - * - * Sets @iter to a valid iterator pointing to @path. - * - * If @path does not exist, @iter is set to an invalid - * iterator and %FALSE is returned. - * - * Returns: %TRUE, if @iter was set - */ -gboolean -gtk_tree_model_get_iter (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreePath *path) -{ - GtkTreeModelIface *iface; - - g_return_val_if_fail (GTK_IS_TREE_MODEL (tree_model), FALSE); - g_return_val_if_fail (iter != NULL, FALSE); - g_return_val_if_fail (path != NULL, FALSE); - - iface = GTK_TREE_MODEL_GET_IFACE (tree_model); - g_return_val_if_fail (iface->get_iter != NULL, FALSE); - g_return_val_if_fail (path->depth > 0, FALSE); - - INITIALIZE_TREE_ITER (iter); - - return (* iface->get_iter) (tree_model, iter, path); -} - -/** - * gtk_tree_model_get_iter_from_string: - * @tree_model: a `GtkTreeModel` - * @iter: (out): an uninitialized `GtkTreeIter` - * @path_string: a string representation of a `GtkTreePath` - * - * Sets @iter to a valid iterator pointing to @path_string, if it - * exists. - * - * Otherwise, @iter is left invalid and %FALSE is returned. - * - * Returns: %TRUE, if @iter was set - */ -gboolean -gtk_tree_model_get_iter_from_string (GtkTreeModel *tree_model, - GtkTreeIter *iter, - const char *path_string) -{ - gboolean retval; - GtkTreePath *path; - - g_return_val_if_fail (GTK_IS_TREE_MODEL (tree_model), FALSE); - g_return_val_if_fail (iter != NULL, FALSE); - g_return_val_if_fail (path_string != NULL, FALSE); - - path = gtk_tree_path_new_from_string (path_string); - - g_return_val_if_fail (path != NULL, FALSE); - - retval = gtk_tree_model_get_iter (tree_model, iter, path); - gtk_tree_path_free (path); - - return retval; -} - -/** - * gtk_tree_model_get_string_from_iter: - * @tree_model: a `GtkTreeModel` - * @iter: a `GtkTreeIter` - * - * Generates a string representation of the iter. - * - * This string is a “:” separated list of numbers. - * For example, “4:10:0:3” would be an acceptable - * return value for this string. - * - * Returns: (nullable): a newly-allocated string - */ -char * -gtk_tree_model_get_string_from_iter (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - GtkTreePath *path; - char *ret; - - g_return_val_if_fail (GTK_IS_TREE_MODEL (tree_model), NULL); - g_return_val_if_fail (iter != NULL, NULL); - - path = gtk_tree_model_get_path (tree_model, iter); - - g_return_val_if_fail (path != NULL, NULL); - - ret = gtk_tree_path_to_string (path); - gtk_tree_path_free (path); - - return ret; -} - -/** - * gtk_tree_model_get_iter_first: - * @tree_model: a `GtkTreeModel` - * @iter: (out): the uninitialized `GtkTreeIter` - * - * Initializes @iter with the first iterator in the tree - * (the one at the path "0"). - * - * Returns %FALSE if the tree is empty, %TRUE otherwise. - * - * Returns: %TRUE, if @iter was set - */ -gboolean -gtk_tree_model_get_iter_first (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - GtkTreePath *path; - gboolean retval; - - g_return_val_if_fail (GTK_IS_TREE_MODEL (tree_model), FALSE); - g_return_val_if_fail (iter != NULL, FALSE); - - path = gtk_tree_path_new_first (); - retval = gtk_tree_model_get_iter (tree_model, iter, path); - gtk_tree_path_free (path); - - return retval; -} - -/** - * gtk_tree_model_get_path: - * @tree_model: a `GtkTreeModel` - * @iter: the `GtkTreeIter` - * - * Returns a newly-created `GtkTreePath` referenced by @iter. - * - * This path should be freed with gtk_tree_path_free(). - * - * Returns: a newly-created `GtkTreePath` - */ -GtkTreePath * -gtk_tree_model_get_path (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - GtkTreeModelIface *iface; - - g_return_val_if_fail (GTK_IS_TREE_MODEL (tree_model), NULL); - g_return_val_if_fail (iter != NULL, NULL); - - iface = GTK_TREE_MODEL_GET_IFACE (tree_model); - g_return_val_if_fail (iface->get_path != NULL, NULL); - - return (* iface->get_path) (tree_model, iter); -} - -/** - * gtk_tree_model_get_value: - * @tree_model: a `GtkTreeModel` - * @iter: the `GtkTreeIter` - * @column: the column to lookup the value at - * @value: (out) (transfer none): an empty `GValue` to set - * - * Initializes and sets @value to that at @column. - * - * When done with @value, g_value_unset() needs to be called - * to free any allocated memory. - */ -void -gtk_tree_model_get_value (GtkTreeModel *tree_model, - GtkTreeIter *iter, - int column, - GValue *value) -{ - GtkTreeModelIface *iface; - - g_return_if_fail (GTK_IS_TREE_MODEL (tree_model)); - g_return_if_fail (iter != NULL); - g_return_if_fail (value != NULL); - - iface = GTK_TREE_MODEL_GET_IFACE (tree_model); - g_return_if_fail (iface->get_value != NULL); - - (* iface->get_value) (tree_model, iter, column, value); -} - -/** - * gtk_tree_model_iter_next: - * @tree_model: a `GtkTreeModel` - * @iter: (in): the `GtkTreeIter` - * - * Sets @iter to point to the node following it at the current level. - * - * If there is no next @iter, %FALSE is returned and @iter is set - * to be invalid. - * - * Returns: %TRUE if @iter has been changed to the next node - */ -gboolean -gtk_tree_model_iter_next (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - GtkTreeModelIface *iface; - - g_return_val_if_fail (GTK_IS_TREE_MODEL (tree_model), FALSE); - g_return_val_if_fail (iter != NULL, FALSE); - - iface = GTK_TREE_MODEL_GET_IFACE (tree_model); - g_return_val_if_fail (iface->iter_next != NULL, FALSE); - - return (* iface->iter_next) (tree_model, iter); -} - -static gboolean -gtk_tree_model_iter_previous_default (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - gboolean retval; - GtkTreePath *path; - - path = gtk_tree_model_get_path (tree_model, iter); - if (path == NULL) - return FALSE; - - retval = gtk_tree_path_prev (path) && - gtk_tree_model_get_iter (tree_model, iter, path); - if (retval == FALSE) - iter->stamp = 0; - - gtk_tree_path_free (path); - - return retval; -} - -/** - * gtk_tree_model_iter_previous: - * @tree_model: a `GtkTreeModel` - * @iter: (in): the `GtkTreeIter` - * - * Sets @iter to point to the previous node at the current level. - * - * If there is no previous @iter, %FALSE is returned and @iter is - * set to be invalid. - * - * Returns: %TRUE if @iter has been changed to the previous node - */ -gboolean -gtk_tree_model_iter_previous (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - gboolean retval; - GtkTreeModelIface *iface; - - g_return_val_if_fail (GTK_IS_TREE_MODEL (tree_model), FALSE); - g_return_val_if_fail (iter != NULL, FALSE); - - iface = GTK_TREE_MODEL_GET_IFACE (tree_model); - - if (iface->iter_previous) - retval = (* iface->iter_previous) (tree_model, iter); - else - retval = gtk_tree_model_iter_previous_default (tree_model, iter); - - return retval; -} - -/** - * gtk_tree_model_iter_children: - * @tree_model: a `GtkTreeModel` - * @iter: (out): the new `GtkTreeIter` to be set to the child - * @parent: (nullable): the `GtkTreeIter` - * - * Sets @iter to point to the first child of @parent. - * - * If @parent has no children, %FALSE is returned and @iter is - * set to be invalid. @parent will remain a valid node after this - * function has been called. - * - * If @parent is %NULL returns the first node, equivalent to - * `gtk_tree_model_get_iter_first (tree_model, iter);` - * - * Returns: %TRUE, if @iter has been set to the first child - */ -gboolean -gtk_tree_model_iter_children (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent) -{ - GtkTreeModelIface *iface; - - g_return_val_if_fail (GTK_IS_TREE_MODEL (tree_model), FALSE); - g_return_val_if_fail (iter != NULL, FALSE); - - iface = GTK_TREE_MODEL_GET_IFACE (tree_model); - g_return_val_if_fail (iface->iter_children != NULL, FALSE); - - INITIALIZE_TREE_ITER (iter); - - return (* iface->iter_children) (tree_model, iter, parent); -} - -/** - * gtk_tree_model_iter_has_child: - * @tree_model: a `GtkTreeModel` - * @iter: the `GtkTreeIter` to test for children - * - * Returns %TRUE if @iter has children, %FALSE otherwise. - * - * Returns: %TRUE if @iter has children - */ -gboolean -gtk_tree_model_iter_has_child (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - GtkTreeModelIface *iface; - - g_return_val_if_fail (GTK_IS_TREE_MODEL (tree_model), FALSE); - g_return_val_if_fail (iter != NULL, FALSE); - - iface = GTK_TREE_MODEL_GET_IFACE (tree_model); - g_return_val_if_fail (iface->iter_has_child != NULL, FALSE); - - return (* iface->iter_has_child) (tree_model, iter); -} - -/** - * gtk_tree_model_iter_n_children: - * @tree_model: a `GtkTreeModel` - * @iter: (nullable): the `GtkTreeIter` - * - * Returns the number of children that @iter has. - * - * As a special case, if @iter is %NULL, then the number - * of toplevel nodes is returned. - * - * Returns: the number of children of @iter - */ -int -gtk_tree_model_iter_n_children (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - GtkTreeModelIface *iface; - - g_return_val_if_fail (GTK_IS_TREE_MODEL (tree_model), 0); - - iface = GTK_TREE_MODEL_GET_IFACE (tree_model); - g_return_val_if_fail (iface->iter_n_children != NULL, 0); - - return (* iface->iter_n_children) (tree_model, iter); -} - -/** - * gtk_tree_model_iter_nth_child: - * @tree_model: a `GtkTreeModel` - * @iter: (out): the `GtkTreeIter` to set to the nth child - * @parent: (nullable): the `GtkTreeIter` to get the child from - * @n: the index of the desired child - * - * Sets @iter to be the child of @parent, using the given index. - * - * The first index is 0. If @n is too big, or @parent has no children, - * @iter is set to an invalid iterator and %FALSE is returned. @parent - * will remain a valid node after this function has been called. As a - * special case, if @parent is %NULL, then the @n-th root node - * is set. - * - * Returns: %TRUE, if @parent has an @n-th child - */ -gboolean -gtk_tree_model_iter_nth_child (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent, - int n) -{ - GtkTreeModelIface *iface; - - g_return_val_if_fail (GTK_IS_TREE_MODEL (tree_model), FALSE); - g_return_val_if_fail (iter != NULL, FALSE); - g_return_val_if_fail (n >= 0, FALSE); - - iface = GTK_TREE_MODEL_GET_IFACE (tree_model); - g_return_val_if_fail (iface->iter_nth_child != NULL, FALSE); - - INITIALIZE_TREE_ITER (iter); - - return (* iface->iter_nth_child) (tree_model, iter, parent, n); -} - -/** - * gtk_tree_model_iter_parent: - * @tree_model: a `GtkTreeModel` - * @iter: (out): the new `GtkTreeIter` to set to the parent - * @child: the `GtkTreeIter` - * - * Sets @iter to be the parent of @child. - * - * If @child is at the toplevel, and doesn’t have a parent, then - * @iter is set to an invalid iterator and %FALSE is returned. - * @child will remain a valid node after this function has been - * called. - * - * @iter will be initialized before the lookup is performed, so @child - * and @iter cannot point to the same memory location. - * - * Returns: %TRUE, if @iter is set to the parent of @child - */ -gboolean -gtk_tree_model_iter_parent (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *child) -{ - GtkTreeModelIface *iface; - - g_return_val_if_fail (GTK_IS_TREE_MODEL (tree_model), FALSE); - g_return_val_if_fail (iter != NULL, FALSE); - g_return_val_if_fail (child != NULL, FALSE); - - iface = GTK_TREE_MODEL_GET_IFACE (tree_model); - g_return_val_if_fail (iface->iter_parent != NULL, FALSE); - - INITIALIZE_TREE_ITER (iter); - - return (* iface->iter_parent) (tree_model, iter, child); -} - -/** - * gtk_tree_model_ref_node: - * @tree_model: a `GtkTreeModel` - * @iter: the `GtkTreeIter` - * - * Lets the tree ref the node. - * - * This is an optional method for models to implement. - * To be more specific, models may ignore this call as it exists - * primarily for performance reasons. - * - * This function is primarily meant as a way for views to let - * caching models know when nodes are being displayed (and hence, - * whether or not to cache that node). Being displayed means a node - * is in an expanded branch, regardless of whether the node is currently - * visible in the viewport. For example, a file-system based model - * would not want to keep the entire file-hierarchy in memory, - * just the sections that are currently being displayed by - * every current view. - * - * A model should be expected to be able to get an iter independent - * of its reffed state. - */ -void -gtk_tree_model_ref_node (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - GtkTreeModelIface *iface; - - g_return_if_fail (GTK_IS_TREE_MODEL (tree_model)); - - iface = GTK_TREE_MODEL_GET_IFACE (tree_model); - if (iface->ref_node) - (* iface->ref_node) (tree_model, iter); -} - -/** - * gtk_tree_model_unref_node: - * @tree_model: a `GtkTreeModel` - * @iter: the `GtkTreeIter` - * - * Lets the tree unref the node. - * - * This is an optional method for models to implement. - * To be more specific, models may ignore this call as it exists - * primarily for performance reasons. For more information on what - * this means, see gtk_tree_model_ref_node(). - * - * Please note that nodes that are deleted are not unreffed. - */ -void -gtk_tree_model_unref_node (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - GtkTreeModelIface *iface; - - g_return_if_fail (GTK_IS_TREE_MODEL (tree_model)); - g_return_if_fail (iter != NULL); - - iface = GTK_TREE_MODEL_GET_IFACE (tree_model); - if (iface->unref_node) - (* iface->unref_node) (tree_model, iter); -} - -/** - * gtk_tree_model_get: - * @tree_model: a `GtkTreeModel` - * @iter: a row in @tree_model - * @...: pairs of column number and value return locations, - * terminated by -1 - * - * Gets the value of one or more cells in the row referenced by @iter. - * - * The variable argument list should contain integer column numbers, - * each column number followed by a place to store the value being - * retrieved. The list is terminated by a -1. For example, to get a - * value from column 0 with type %G_TYPE_STRING, you would - * write: `gtk_tree_model_get (model, iter, 0, &place_string_here, -1)`, - * where `place_string_here` is a #gchararray - * to be filled with the string. - * - * Returned values with type %G_TYPE_OBJECT have to be unreferenced, - * values with type %G_TYPE_STRING or %G_TYPE_BOXED have to be freed. - * Other values are passed by value. - */ -void -gtk_tree_model_get (GtkTreeModel *tree_model, - GtkTreeIter *iter, - ...) -{ - va_list var_args; - - g_return_if_fail (GTK_IS_TREE_MODEL (tree_model)); - g_return_if_fail (iter != NULL); - - va_start (var_args, iter); - gtk_tree_model_get_valist (tree_model, iter, var_args); - va_end (var_args); -} - -/** - * gtk_tree_model_get_valist: - * @tree_model: a `GtkTreeModel` - * @iter: a row in @tree_model - * @var_args: va_list of column/return location pairs - * - * Gets the value of one or more cells in the row referenced by @iter. - * - * See [method@Gtk.TreeModel.get], this version takes a va_list - * for language bindings to use. - */ -void -gtk_tree_model_get_valist (GtkTreeModel *tree_model, - GtkTreeIter *iter, - va_list var_args) -{ - int column; - - g_return_if_fail (GTK_IS_TREE_MODEL (tree_model)); - g_return_if_fail (iter != NULL); - - column = va_arg (var_args, int); - - while (column != -1) - { - GValue value = G_VALUE_INIT; - char *error = NULL; - - if (column >= gtk_tree_model_get_n_columns (tree_model)) - { - g_warning ("%s: Invalid column number %d accessed (remember to end your list of columns with a -1)", G_STRLOC, column); - break; - } - - gtk_tree_model_get_value (GTK_TREE_MODEL (tree_model), iter, column, &value); - - G_VALUE_LCOPY (&value, var_args, 0, &error); - if (error) - { - g_warning ("%s: %s", G_STRLOC, error); - g_free (error); - - /* we purposely leak the value here, it might not be - * in a sane state if an error condition occurred - */ - break; - } - - g_value_unset (&value); - - column = va_arg (var_args, int); - } -} - -/** - * gtk_tree_model_row_changed: - * @tree_model: a `GtkTreeModel` - * @path: a `GtkTreePath` pointing to the changed row - * @iter: a valid `GtkTreeIter` pointing to the changed row - * - * Emits the ::row-changed signal on @tree_model. - * - * See [signal@Gtk.TreeModel::row-changed]. - */ -void -gtk_tree_model_row_changed (GtkTreeModel *tree_model, - GtkTreePath *path, - GtkTreeIter *iter) -{ - g_return_if_fail (GTK_IS_TREE_MODEL (tree_model)); - g_return_if_fail (path != NULL); - g_return_if_fail (iter != NULL); - - g_signal_emit (tree_model, tree_model_signals[ROW_CHANGED], 0, path, iter); -} - -/** - * gtk_tree_model_row_inserted: - * @tree_model: a `GtkTreeModel` - * @path: a `GtkTreePath` pointing to the inserted row - * @iter: a valid `GtkTreeIter` pointing to the inserted row - * - * Emits the ::row-inserted signal on @tree_model. - * - * See [signal@Gtk.TreeModel::row-inserted]. - */ -void -gtk_tree_model_row_inserted (GtkTreeModel *tree_model, - GtkTreePath *path, - GtkTreeIter *iter) -{ - g_return_if_fail (GTK_IS_TREE_MODEL (tree_model)); - g_return_if_fail (path != NULL); - g_return_if_fail (iter != NULL); - - g_signal_emit (tree_model, tree_model_signals[ROW_INSERTED], 0, path, iter); -} - -/** - * gtk_tree_model_row_has_child_toggled: - * @tree_model: a `GtkTreeModel` - * @path: a `GtkTreePath` pointing to the changed row - * @iter: a valid `GtkTreeIter` pointing to the changed row - * - * Emits the ::row-has-child-toggled signal on @tree_model. - * - * See [signal@Gtk.TreeModel::row-has-child-toggled]. - * - * This should be called by models after the child - * state of a node changes. - */ -void -gtk_tree_model_row_has_child_toggled (GtkTreeModel *tree_model, - GtkTreePath *path, - GtkTreeIter *iter) -{ - g_return_if_fail (GTK_IS_TREE_MODEL (tree_model)); - g_return_if_fail (path != NULL); - g_return_if_fail (iter != NULL); - - g_signal_emit (tree_model, tree_model_signals[ROW_HAS_CHILD_TOGGLED], 0, path, iter); -} - -/** - * gtk_tree_model_row_deleted: - * @tree_model: a `GtkTreeModel` - * @path: a `GtkTreePath` pointing to the previous location of - * the deleted row - * - * Emits the ::row-deleted signal on @tree_model. - * - * See [signal@Gtk.TreeModel::row-deleted]. - * - * This should be called by models after a row has been removed. - * The location pointed to by @path should be the location that - * the row previously was at. It may not be a valid location anymore. - * - * Nodes that are deleted are not unreffed, this means that any - * outstanding references on the deleted node should not be released. - */ -void -gtk_tree_model_row_deleted (GtkTreeModel *tree_model, - GtkTreePath *path) -{ - g_return_if_fail (GTK_IS_TREE_MODEL (tree_model)); - g_return_if_fail (path != NULL); - - g_signal_emit (tree_model, tree_model_signals[ROW_DELETED], 0, path); -} - -/** - * gtk_tree_model_rows_reordered: (skip) - * @tree_model: a `GtkTreeModel` - * @path: a `GtkTreePath` pointing to the tree node whose children - * have been reordered - * @iter: a valid `GtkTreeIter` pointing to the node whose children - * have been reordered, or %NULL if the depth of @path is 0 - * @new_order: an array of integers mapping the current position of - * each child to its old position before the re-ordering, - * i.e. @new_order`[newpos] = oldpos` - * - * Emits the ::rows-reordered signal on @tree_model. - * - * See [signal@Gtk.TreeModel::rows-reordered]. - * - * This should be called by models when their rows have been - * reordered. - */ -void -gtk_tree_model_rows_reordered (GtkTreeModel *tree_model, - GtkTreePath *path, - GtkTreeIter *iter, - int *new_order) -{ - g_return_if_fail (GTK_IS_TREE_MODEL (tree_model)); - g_return_if_fail (new_order != NULL); - - g_signal_emit (tree_model, tree_model_signals[ROWS_REORDERED], 0, path, iter, new_order); -} - -/** - * gtk_tree_model_rows_reordered_with_length: (rename-to gtk_tree_model_rows_reordered) - * @tree_model: a `GtkTreeModel` - * @path: a `GtkTreePath` pointing to the tree node whose children - * have been reordered - * @iter: (nullable): a valid `GtkTreeIter` pointing to the node - * whose children have been reordered, or %NULL if the depth - * of @path is 0 - * @new_order: (array length=length): an array of integers - * mapping the current position of each child to its old - * position before the re-ordering, - * i.e. @new_order`[newpos] = oldpos` - * @length: length of @new_order array - * - * Emits the ::rows-reordered signal on @tree_model. - * - * See [signal@Gtk.TreeModel::rows-reordered]. - * - * This should be called by models when their rows have been - * reordered. - */ -void -gtk_tree_model_rows_reordered_with_length (GtkTreeModel *tree_model, - GtkTreePath *path, - GtkTreeIter *iter, - int *new_order, - int length) -{ - g_return_if_fail (GTK_IS_TREE_MODEL (tree_model)); - g_return_if_fail (new_order != NULL); - g_return_if_fail (length == gtk_tree_model_iter_n_children (tree_model, iter)); - - g_signal_emit (tree_model, tree_model_signals[ROWS_REORDERED], 0, path, iter, new_order); -} - -static gboolean -gtk_tree_model_foreach_helper (GtkTreeModel *model, - GtkTreeIter *iter, - GtkTreePath *path, - GtkTreeModelForeachFunc func, - gpointer user_data) -{ - gboolean iters_persist; - - iters_persist = gtk_tree_model_get_flags (model) & GTK_TREE_MODEL_ITERS_PERSIST; - - do - { - GtkTreeIter child; - - if ((* func) (model, path, iter, user_data)) - return TRUE; - - if (!iters_persist) - { - if (!gtk_tree_model_get_iter (model, iter, path)) - return TRUE; - } - - if (gtk_tree_model_iter_children (model, &child, iter)) - { - gtk_tree_path_down (path); - if (gtk_tree_model_foreach_helper (model, &child, path, func, user_data)) - return TRUE; - gtk_tree_path_up (path); - } - - gtk_tree_path_next (path); - } - while (gtk_tree_model_iter_next (model, iter)); - - return FALSE; -} - -/** - * gtk_tree_model_foreach: - * @model: a `GtkTreeModel` - * @func: (scope call): a function to be called on each row - * @user_data: (closure): user data to passed to @func - * - * Calls @func on each node in model in a depth-first fashion. - * - * If @func returns %TRUE, then the tree ceases to be walked, - * and gtk_tree_model_foreach() returns. - */ -void -gtk_tree_model_foreach (GtkTreeModel *model, - GtkTreeModelForeachFunc func, - gpointer user_data) -{ - GtkTreePath *path; - GtkTreeIter iter; - - g_return_if_fail (GTK_IS_TREE_MODEL (model)); - g_return_if_fail (func != NULL); - - path = gtk_tree_path_new_first (); - if (!gtk_tree_model_get_iter (model, &iter, path)) - { - gtk_tree_path_free (path); - return; - } - - gtk_tree_model_foreach_helper (model, &iter, path, func, user_data); - gtk_tree_path_free (path); -} - - -/* - * GtkTreeRowReference - */ - -static void gtk_tree_row_reference_unref_path (GtkTreePath *path, - GtkTreeModel *model, - int depth); - - -G_DEFINE_BOXED_TYPE (GtkTreeRowReference, gtk_tree_row_reference, - gtk_tree_row_reference_copy, - gtk_tree_row_reference_free) - -struct _GtkTreeRowReference -{ - GObject *proxy; - GtkTreeModel *model; - GtkTreePath *path; -}; - - -static void -release_row_references (gpointer data) -{ - RowRefList *refs = data; - GSList *tmp_list = NULL; - - tmp_list = refs->list; - while (tmp_list != NULL) - { - GtkTreeRowReference *reference = tmp_list->data; - - if (reference->proxy == (GObject *)reference->model) - reference->model = NULL; - reference->proxy = NULL; - - /* we don't free the reference, users are responsible for that. */ - - tmp_list = tmp_list->next; - } - - g_slist_free (refs->list); - g_free (refs); -} - -static void -gtk_tree_row_ref_inserted (RowRefList *refs, - GtkTreePath *path, - GtkTreeIter *iter) -{ - GSList *tmp_list; - - if (refs == NULL) - return; - - /* This function corrects the path stored in the reference to - * account for an insertion. Note that it's called _after_ the - * insertion with the path to the newly-inserted row. Which means - * that the inserted path is in a different "coordinate system" than - * the old path (e.g. if the inserted path was just before the old - * path, then inserted path and old path will be the same, and old - * path must be moved down one). - */ - - tmp_list = refs->list; - - while (tmp_list != NULL) - { - GtkTreeRowReference *reference = tmp_list->data; - - if (reference->path == NULL) - goto done; - - if (reference->path->depth >= path->depth) - { - int i; - gboolean ancestor = TRUE; - - for (i = 0; i < path->depth - 1; i ++) - { - if (path->indices[i] != reference->path->indices[i]) - { - ancestor = FALSE; - break; - } - } - if (ancestor == FALSE) - goto done; - - if (path->indices[path->depth-1] <= reference->path->indices[path->depth-1]) - reference->path->indices[path->depth-1] += 1; - } - done: - tmp_list = tmp_list->next; - } -} - -static void -gtk_tree_row_ref_deleted (RowRefList *refs, - GtkTreePath *path) -{ - GSList *tmp_list; - - if (refs == NULL) - return; - - /* This function corrects the path stored in the reference to - * account for a deletion. Note that it's called _after_ the - * deletion with the old path of the just-deleted row. Which means - * that the deleted path is the same now-defunct "coordinate system" - * as the path saved in the reference, which is what we want to fix. - */ - - tmp_list = refs->list; - - while (tmp_list != NULL) - { - GtkTreeRowReference *reference = tmp_list->data; - - if (reference->path) - { - int i; - - if (path->depth > reference->path->depth) - goto next; - for (i = 0; i < path->depth - 1; i++) - { - if (path->indices[i] != reference->path->indices[i]) - goto next; - } - - /* We know it affects us. */ - if (path->indices[i] == reference->path->indices[i]) - { - if (reference->path->depth > path->depth) - /* some parent was deleted, trying to unref any node - * between the deleted parent and the node the reference - * is pointing to is bad, as those nodes are already gone. - */ - gtk_tree_row_reference_unref_path (reference->path, reference->model, path->depth - 1); - else - gtk_tree_row_reference_unref_path (reference->path, reference->model, reference->path->depth - 1); - gtk_tree_path_free (reference->path); - reference->path = NULL; - } - else if (path->indices[i] < reference->path->indices[i]) - { - reference->path->indices[path->depth-1]-=1; - } - } - -next: - tmp_list = tmp_list->next; - } -} - -static void -gtk_tree_row_ref_reordered (RowRefList *refs, - GtkTreePath *path, - GtkTreeIter *iter, - int *new_order) -{ - GSList *tmp_list; - int length; - - if (refs == NULL) - return; - - tmp_list = refs->list; - - while (tmp_list != NULL) - { - GtkTreeRowReference *reference = tmp_list->data; - - length = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (reference->model), iter); - - if (length < 2) - return; - - if ((reference->path) && - (gtk_tree_path_is_ancestor (path, reference->path))) - { - int ref_depth = gtk_tree_path_get_depth (reference->path); - int depth = gtk_tree_path_get_depth (path); - - if (ref_depth > depth) - { - int i; - int *indices = gtk_tree_path_get_indices (reference->path); - - for (i = 0; i < length; i++) - { - if (new_order[i] == indices[depth]) - { - indices[depth] = i; - break; - } - } - } - } - - tmp_list = tmp_list->next; - } -} - -/* We do this recursively so that we can unref children nodes - * before their parent - */ -static void -gtk_tree_row_reference_unref_path_helper (GtkTreePath *path, - GtkTreeModel *model, - GtkTreeIter *parent_iter, - int depth, - int current_depth) -{ - GtkTreeIter iter; - - if (depth == current_depth) - return; - - gtk_tree_model_iter_nth_child (model, &iter, parent_iter, path->indices[current_depth]); - gtk_tree_row_reference_unref_path_helper (path, model, &iter, depth, current_depth + 1); - gtk_tree_model_unref_node (model, &iter); -} - -static void -gtk_tree_row_reference_unref_path (GtkTreePath *path, - GtkTreeModel *model, - int depth) -{ - GtkTreeIter iter; - - if (depth <= 0) - return; - - gtk_tree_model_iter_nth_child (model, &iter, NULL, path->indices[0]); - gtk_tree_row_reference_unref_path_helper (path, model, &iter, depth, 1); - gtk_tree_model_unref_node (model, &iter); -} - -/** - * gtk_tree_row_reference_new: - * @model: a `GtkTreeModel` - * @path: a valid `GtkTreePath` to monitor - * - * Creates a row reference based on @path. - * - * This reference will keep pointing to the node pointed to - * by @path, so long as it exists. Any changes that occur on @model are - * propagated, and the path is updated appropriately. If - * @path isn’t a valid path in @model, then %NULL is returned. - * - * Returns: (nullable): a newly allocated `GtkTreeRowReference` - */ -GtkTreeRowReference * -gtk_tree_row_reference_new (GtkTreeModel *model, - GtkTreePath *path) -{ - g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL); - g_return_val_if_fail (path != NULL, NULL); - - /* We use the model itself as the proxy object; and call - * gtk_tree_row_reference_inserted(), etc, in the - * class closure (default handler) marshalers for the signal. - */ - return gtk_tree_row_reference_new_proxy (G_OBJECT (model), model, path); -} - -/** - * gtk_tree_row_reference_new_proxy: - * @proxy: a proxy `GObject` - * @model: a `GtkTreeModel` - * @path: a valid `GtkTreePath` to monitor - * - * You do not need to use this function. - * - * Creates a row reference based on @path. - * - * This reference will keep pointing to the node pointed to - * by @path, so long as it exists. If @path isn’t a valid - * path in @model, then %NULL is returned. However, unlike - * references created with gtk_tree_row_reference_new(), it - * does not listen to the model for changes. The creator of - * the row reference must do this explicitly using - * gtk_tree_row_reference_inserted(), gtk_tree_row_reference_deleted(), - * gtk_tree_row_reference_reordered(). - * - * These functions must be called exactly once per proxy when the - * corresponding signal on the model is emitted. This single call - * updates all row references for that proxy. Since built-in GTK - * objects like `GtkTreeView` already use this mechanism internally, - * using them as the proxy object will produce unpredictable results. - * Further more, passing the same object as @model and @proxy - * doesn’t work for reasons of internal implementation. - * - * This type of row reference is primarily meant by structures that - * need to carefully monitor exactly when a row reference updates - * itself, and is not generally needed by most applications. - * - * Returns: (nullable): a newly allocated `GtkTreeRowReference` - */ -GtkTreeRowReference * -gtk_tree_row_reference_new_proxy (GObject *proxy, - GtkTreeModel *model, - GtkTreePath *path) -{ - GtkTreeRowReference *reference; - RowRefList *refs; - GtkTreeIter parent_iter; - int i; - - g_return_val_if_fail (G_IS_OBJECT (proxy), NULL); - g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL); - g_return_val_if_fail (path != NULL, NULL); - g_return_val_if_fail (path->depth > 0, NULL); - - /* check that the path is valid */ - if (gtk_tree_model_get_iter (model, &parent_iter, path) == FALSE) - return NULL; - - /* Now we want to ref every node */ - gtk_tree_model_iter_nth_child (model, &parent_iter, NULL, path->indices[0]); - gtk_tree_model_ref_node (model, &parent_iter); - - for (i = 1; i < path->depth; i++) - { - GtkTreeIter iter; - gtk_tree_model_iter_nth_child (model, &iter, &parent_iter, path->indices[i]); - gtk_tree_model_ref_node (model, &iter); - parent_iter = iter; - } - - /* Make the row reference */ - reference = g_new (GtkTreeRowReference, 1); - - g_object_ref (proxy); - g_object_ref (model); - reference->proxy = proxy; - reference->model = model; - reference->path = gtk_tree_path_copy (path); - - refs = g_object_get_data (G_OBJECT (proxy), ROW_REF_DATA_STRING); - - if (refs == NULL) - { - refs = g_new (RowRefList, 1); - refs->list = NULL; - - g_object_set_data_full (G_OBJECT (proxy), - I_(ROW_REF_DATA_STRING), - refs, release_row_references); - } - - refs->list = g_slist_prepend (refs->list, reference); - - return reference; -} - -/** - * gtk_tree_row_reference_get_path: - * @reference: a `GtkTreeRowReference` - * - * Returns a path that the row reference currently points to, - * or %NULL if the path pointed to is no longer valid. - * - * Returns: (nullable) (transfer full): a current path - */ -GtkTreePath * -gtk_tree_row_reference_get_path (GtkTreeRowReference *reference) -{ - g_return_val_if_fail (reference != NULL, NULL); - - if (reference->proxy == NULL) - return NULL; - - if (reference->path == NULL) - return NULL; - - return gtk_tree_path_copy (reference->path); -} - -/** - * gtk_tree_row_reference_get_model: - * @reference: a `GtkTreeRowReference` - * - * Returns the model that the row reference is monitoring. - * - * Returns: (transfer none): the model - */ -GtkTreeModel * -gtk_tree_row_reference_get_model (GtkTreeRowReference *reference) -{ - g_return_val_if_fail (reference != NULL, NULL); - - return reference->model; -} - -/** - * gtk_tree_row_reference_valid: - * @reference: (nullable): a `GtkTreeRowReference` - * - * Returns %TRUE if the @reference is non-%NULL and refers to - * a current valid path. - * - * Returns: %TRUE if @reference points to a valid path - */ -gboolean -gtk_tree_row_reference_valid (GtkTreeRowReference *reference) -{ - if (reference == NULL || reference->path == NULL) - return FALSE; - - return TRUE; -} - - -/** - * gtk_tree_row_reference_copy: - * @reference: a `GtkTreeRowReference` - * - * Copies a `GtkTreeRowReference`. - * - * Returns: a copy of @reference - */ -GtkTreeRowReference * -gtk_tree_row_reference_copy (GtkTreeRowReference *reference) -{ - return gtk_tree_row_reference_new_proxy (reference->proxy, - reference->model, - reference->path); -} - -/** - * gtk_tree_row_reference_free: - * @reference: (nullable): a `GtkTreeRowReference` - * - * Free’s @reference. @reference may be %NULL - */ -void -gtk_tree_row_reference_free (GtkTreeRowReference *reference) -{ - RowRefList *refs; - - if (reference == NULL) - return; - - refs = g_object_get_data (G_OBJECT (reference->proxy), ROW_REF_DATA_STRING); - - if (refs == NULL) - { - g_warning (G_STRLOC": bad row reference, proxy has no outstanding row references"); - return; - } - - refs->list = g_slist_remove (refs->list, reference); - - if (refs->list == NULL) - { - g_object_set_data (G_OBJECT (reference->proxy), - I_(ROW_REF_DATA_STRING), - NULL); - } - - if (reference->path) - { - gtk_tree_row_reference_unref_path (reference->path, reference->model, reference->path->depth); - gtk_tree_path_free (reference->path); - } - - g_object_unref (reference->proxy); - g_object_unref (reference->model); - g_free (reference); -} - -/** - * gtk_tree_row_reference_inserted: - * @proxy: a `GObject` - * @path: the row position that was inserted - * - * Lets a set of row reference created by - * gtk_tree_row_reference_new_proxy() know that the - * model emitted the ::row-inserted signal. - */ -void -gtk_tree_row_reference_inserted (GObject *proxy, - GtkTreePath *path) -{ - g_return_if_fail (G_IS_OBJECT (proxy)); - - gtk_tree_row_ref_inserted ((RowRefList *)g_object_get_data (proxy, ROW_REF_DATA_STRING), path, NULL); -} - -/** - * gtk_tree_row_reference_deleted: - * @proxy: a `GObject` - * @path: the path position that was deleted - * - * Lets a set of row reference created by - * gtk_tree_row_reference_new_proxy() know that the - * model emitted the ::row-deleted signal. - */ -void -gtk_tree_row_reference_deleted (GObject *proxy, - GtkTreePath *path) -{ - g_return_if_fail (G_IS_OBJECT (proxy)); - - gtk_tree_row_ref_deleted ((RowRefList *)g_object_get_data (proxy, ROW_REF_DATA_STRING), path); -} - -/** - * gtk_tree_row_reference_reordered: (skip) - * @proxy: a `GObject` - * @path: the parent path of the reordered signal - * @iter: the iter pointing to the parent of the reordered - * @new_order: (array): the new order of rows - * - * Lets a set of row reference created by - * gtk_tree_row_reference_new_proxy() know that the - * model emitted the ::rows-reordered signal. - */ -void -gtk_tree_row_reference_reordered (GObject *proxy, - GtkTreePath *path, - GtkTreeIter *iter, - int *new_order) -{ - g_return_if_fail (G_IS_OBJECT (proxy)); - - gtk_tree_row_ref_reordered ((RowRefList *)g_object_get_data (proxy, ROW_REF_DATA_STRING), path, iter, new_order); -} diff --git a/gtk/gtktreemodel.h b/gtk/gtktreemodel.h deleted file mode 100644 index 1ee15ca59c..0000000000 --- a/gtk/gtktreemodel.h +++ /dev/null @@ -1,413 +0,0 @@ -/* gtktreemodel.h - * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#ifndef __GTK_TREE_MODEL_H__ -#define __GTK_TREE_MODEL_H__ - -#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) -#error "Only can be included directly." -#endif - -#include -#include - -G_BEGIN_DECLS - -#define GTK_TYPE_TREE_MODEL (gtk_tree_model_get_type ()) -#define GTK_TREE_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_TREE_MODEL, GtkTreeModel)) -#define GTK_IS_TREE_MODEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_TREE_MODEL)) -#define GTK_TREE_MODEL_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GTK_TYPE_TREE_MODEL, GtkTreeModelIface)) - -#define GTK_TYPE_TREE_ITER (gtk_tree_iter_get_type ()) -#define GTK_TYPE_TREE_PATH (gtk_tree_path_get_type ()) -#define GTK_TYPE_TREE_ROW_REFERENCE (gtk_tree_row_reference_get_type ()) - -typedef struct _GtkTreeIter GtkTreeIter; -typedef struct _GtkTreePath GtkTreePath; -typedef struct _GtkTreeRowReference GtkTreeRowReference; -typedef struct _GtkTreeModel GtkTreeModel; /* Dummy typedef */ -typedef struct _GtkTreeModelIface GtkTreeModelIface; - -/** - * GtkTreeModelForeachFunc: - * @model: the `GtkTreeModel` being iterated - * @path: the current `GtkTreePath` - * @iter: the current `GtkTreeIter` - * @data: (closure): The user data passed to gtk_tree_model_foreach() - * - * Type of the callback passed to gtk_tree_model_foreach() to - * iterate over the rows in a tree model. - * - * Returns: %TRUE to stop iterating, %FALSE to continue - * - */ -typedef gboolean (* GtkTreeModelForeachFunc) (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data); - -/** - * GtkTreeModelFlags: - * @GTK_TREE_MODEL_ITERS_PERSIST: iterators survive all signals - * emitted by the tree - * @GTK_TREE_MODEL_LIST_ONLY: the model is a list only, and never - * has children - * - * These flags indicate various properties of a `GtkTreeModel`. - * - * They are returned by [method@Gtk.TreeModel.get_flags], and must be - * static for the lifetime of the object. A more complete description - * of %GTK_TREE_MODEL_ITERS_PERSIST can be found in the overview of - * this section. - */ -typedef enum -{ - GTK_TREE_MODEL_ITERS_PERSIST = 1 << 0, - GTK_TREE_MODEL_LIST_ONLY = 1 << 1 -} GtkTreeModelFlags; - -/** - * GtkTreeIter: - * @stamp: a unique stamp to catch invalid iterators - * @user_data: model-specific data - * @user_data2: model-specific data - * @user_data3: model-specific data - * - * The `GtkTreeIter` is the primary structure - * for accessing a `GtkTreeModel`. Models are expected to put a unique - * integer in the @stamp member, and put - * model-specific data in the three @user_data - * members. - */ -struct _GtkTreeIter -{ - int stamp; - gpointer user_data; - gpointer user_data2; - gpointer user_data3; -}; - -/** - * GtkTreeModelIface: - * @row_changed: Signal emitted when a row in the model has changed. - * @row_inserted: Signal emitted when a new row has been inserted in - * the model. - * @row_has_child_toggled: Signal emitted when a row has gotten the - * first child row or lost its last child row. - * @row_deleted: Signal emitted when a row has been deleted. - * @rows_reordered: Signal emitted when the children of a node in the - * GtkTreeModel have been reordered. - * @get_flags: Get `GtkTreeModelFlags` supported by this interface. - * @get_n_columns: Get the number of columns supported by the model. - * @get_column_type: Get the type of the column. - * @get_iter: Sets iter to a valid iterator pointing to path. - * @get_path: Gets a newly-created `GtkTreePath` referenced by iter. - * @get_value: Initializes and sets value to that at column. - * @iter_next: Sets iter to point to the node following it at the - * current level. - * @iter_previous: Sets iter to point to the previous node at the - * current level. - * @iter_children: Sets iter to point to the first child of parent. - * @iter_has_child: %TRUE if iter has children, %FALSE otherwise. - * @iter_n_children: Gets the number of children that iter has. - * @iter_nth_child: Sets iter to be the child of parent, using the - * given index. - * @iter_parent: Sets iter to be the parent of child. - * @ref_node: Lets the tree ref the node. - * @unref_node: Lets the tree unref the node. - */ -struct _GtkTreeModelIface -{ - /*< private >*/ - GTypeInterface g_iface; - - /*< public >*/ - - /* Signals */ - void (* row_changed) (GtkTreeModel *tree_model, - GtkTreePath *path, - GtkTreeIter *iter); - void (* row_inserted) (GtkTreeModel *tree_model, - GtkTreePath *path, - GtkTreeIter *iter); - void (* row_has_child_toggled) (GtkTreeModel *tree_model, - GtkTreePath *path, - GtkTreeIter *iter); - void (* row_deleted) (GtkTreeModel *tree_model, - GtkTreePath *path); - void (* rows_reordered) (GtkTreeModel *tree_model, - GtkTreePath *path, - GtkTreeIter *iter, - int *new_order); - - /* Virtual Table */ - GtkTreeModelFlags (* get_flags) (GtkTreeModel *tree_model); - - int (* get_n_columns) (GtkTreeModel *tree_model); - GType (* get_column_type) (GtkTreeModel *tree_model, - int index_); - gboolean (* get_iter) (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreePath *path); - GtkTreePath *(* get_path) (GtkTreeModel *tree_model, - GtkTreeIter *iter); - void (* get_value) (GtkTreeModel *tree_model, - GtkTreeIter *iter, - int column, - GValue *value); - gboolean (* iter_next) (GtkTreeModel *tree_model, - GtkTreeIter *iter); - gboolean (* iter_previous) (GtkTreeModel *tree_model, - GtkTreeIter *iter); - gboolean (* iter_children) (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent); - gboolean (* iter_has_child) (GtkTreeModel *tree_model, - GtkTreeIter *iter); - int (* iter_n_children) (GtkTreeModel *tree_model, - GtkTreeIter *iter); - gboolean (* iter_nth_child) (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent, - int n); - gboolean (* iter_parent) (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *child); - void (* ref_node) (GtkTreeModel *tree_model, - GtkTreeIter *iter); - void (* unref_node) (GtkTreeModel *tree_model, - GtkTreeIter *iter); -}; - - -/* GtkTreePath operations */ -GDK_AVAILABLE_IN_ALL -GtkTreePath *gtk_tree_path_new (void); -GDK_AVAILABLE_IN_ALL -GtkTreePath *gtk_tree_path_new_from_string (const char *path); -GDK_AVAILABLE_IN_ALL -GtkTreePath *gtk_tree_path_new_from_indices (int first_index, - ...); -GDK_AVAILABLE_IN_ALL -GtkTreePath *gtk_tree_path_new_from_indicesv (int *indices, - gsize length); -GDK_AVAILABLE_IN_ALL -char *gtk_tree_path_to_string (GtkTreePath *path); -GDK_AVAILABLE_IN_ALL -GtkTreePath *gtk_tree_path_new_first (void); -GDK_AVAILABLE_IN_ALL -void gtk_tree_path_append_index (GtkTreePath *path, - int index_); -GDK_AVAILABLE_IN_ALL -void gtk_tree_path_prepend_index (GtkTreePath *path, - int index_); -GDK_AVAILABLE_IN_ALL -int gtk_tree_path_get_depth (GtkTreePath *path); -GDK_AVAILABLE_IN_ALL -int *gtk_tree_path_get_indices (GtkTreePath *path); - -GDK_AVAILABLE_IN_ALL -int *gtk_tree_path_get_indices_with_depth (GtkTreePath *path, - int *depth); - -GDK_AVAILABLE_IN_ALL -void gtk_tree_path_free (GtkTreePath *path); -GDK_AVAILABLE_IN_ALL -GtkTreePath *gtk_tree_path_copy (const GtkTreePath *path); -GDK_AVAILABLE_IN_ALL -GType gtk_tree_path_get_type (void) G_GNUC_CONST; -GDK_AVAILABLE_IN_ALL -int gtk_tree_path_compare (const GtkTreePath *a, - const GtkTreePath *b); -GDK_AVAILABLE_IN_ALL -void gtk_tree_path_next (GtkTreePath *path); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_path_prev (GtkTreePath *path); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_path_up (GtkTreePath *path); -GDK_AVAILABLE_IN_ALL -void gtk_tree_path_down (GtkTreePath *path); - -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_path_is_ancestor (GtkTreePath *path, - GtkTreePath *descendant); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_path_is_descendant (GtkTreePath *path, - GtkTreePath *ancestor); - -/** - * GtkTreeRowReference: - * - * A GtkTreeRowReference tracks model changes so that it always refers to the - * same row (a `GtkTreePath` refers to a position, not a fixed row). Create a - * new GtkTreeRowReference with gtk_tree_row_reference_new(). - */ - -GDK_AVAILABLE_IN_ALL -GType gtk_tree_row_reference_get_type (void) G_GNUC_CONST; -GDK_AVAILABLE_IN_ALL -GtkTreeRowReference *gtk_tree_row_reference_new (GtkTreeModel *model, - GtkTreePath *path); -GDK_AVAILABLE_IN_ALL -GtkTreeRowReference *gtk_tree_row_reference_new_proxy (GObject *proxy, - GtkTreeModel *model, - GtkTreePath *path); -GDK_AVAILABLE_IN_ALL -GtkTreePath *gtk_tree_row_reference_get_path (GtkTreeRowReference *reference); -GDK_AVAILABLE_IN_ALL -GtkTreeModel *gtk_tree_row_reference_get_model (GtkTreeRowReference *reference); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_row_reference_valid (GtkTreeRowReference *reference); -GDK_AVAILABLE_IN_ALL -GtkTreeRowReference *gtk_tree_row_reference_copy (GtkTreeRowReference *reference); -GDK_AVAILABLE_IN_ALL -void gtk_tree_row_reference_free (GtkTreeRowReference *reference); -/* These two functions are only needed if you created the row reference with a - * proxy object */ -GDK_AVAILABLE_IN_ALL -void gtk_tree_row_reference_inserted (GObject *proxy, - GtkTreePath *path); -GDK_AVAILABLE_IN_ALL -void gtk_tree_row_reference_deleted (GObject *proxy, - GtkTreePath *path); -GDK_AVAILABLE_IN_ALL -void gtk_tree_row_reference_reordered (GObject *proxy, - GtkTreePath *path, - GtkTreeIter *iter, - int *new_order); - -/* GtkTreeIter operations */ -GDK_AVAILABLE_IN_ALL -GtkTreeIter * gtk_tree_iter_copy (GtkTreeIter *iter); -GDK_AVAILABLE_IN_ALL -void gtk_tree_iter_free (GtkTreeIter *iter); -GDK_AVAILABLE_IN_ALL -GType gtk_tree_iter_get_type (void) G_GNUC_CONST; - -GDK_AVAILABLE_IN_ALL -GType gtk_tree_model_get_type (void) G_GNUC_CONST; -GDK_AVAILABLE_IN_ALL -GtkTreeModelFlags gtk_tree_model_get_flags (GtkTreeModel *tree_model); -GDK_AVAILABLE_IN_ALL -int gtk_tree_model_get_n_columns (GtkTreeModel *tree_model); -GDK_AVAILABLE_IN_ALL -GType gtk_tree_model_get_column_type (GtkTreeModel *tree_model, - int index_); - - -/* Iterator movement */ -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_model_get_iter (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreePath *path); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_model_get_iter_from_string (GtkTreeModel *tree_model, - GtkTreeIter *iter, - const char *path_string); -GDK_AVAILABLE_IN_ALL -char * gtk_tree_model_get_string_from_iter (GtkTreeModel *tree_model, - GtkTreeIter *iter); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_model_get_iter_first (GtkTreeModel *tree_model, - GtkTreeIter *iter); -GDK_AVAILABLE_IN_ALL -GtkTreePath * gtk_tree_model_get_path (GtkTreeModel *tree_model, - GtkTreeIter *iter); -GDK_AVAILABLE_IN_ALL -void gtk_tree_model_get_value (GtkTreeModel *tree_model, - GtkTreeIter *iter, - int column, - GValue *value); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_model_iter_previous (GtkTreeModel *tree_model, - GtkTreeIter *iter); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_model_iter_next (GtkTreeModel *tree_model, - GtkTreeIter *iter); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_model_iter_children (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_model_iter_has_child (GtkTreeModel *tree_model, - GtkTreeIter *iter); -GDK_AVAILABLE_IN_ALL -int gtk_tree_model_iter_n_children (GtkTreeModel *tree_model, - GtkTreeIter *iter); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_model_iter_nth_child (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent, - int n); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_model_iter_parent (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *child); -GDK_AVAILABLE_IN_ALL -void gtk_tree_model_ref_node (GtkTreeModel *tree_model, - GtkTreeIter *iter); -GDK_AVAILABLE_IN_ALL -void gtk_tree_model_unref_node (GtkTreeModel *tree_model, - GtkTreeIter *iter); -GDK_AVAILABLE_IN_ALL -void gtk_tree_model_get (GtkTreeModel *tree_model, - GtkTreeIter *iter, - ...); -GDK_AVAILABLE_IN_ALL -void gtk_tree_model_get_valist (GtkTreeModel *tree_model, - GtkTreeIter *iter, - va_list var_args); - - -GDK_AVAILABLE_IN_ALL -void gtk_tree_model_foreach (GtkTreeModel *model, - GtkTreeModelForeachFunc func, - gpointer user_data); - -/* Signals */ -GDK_AVAILABLE_IN_ALL -void gtk_tree_model_row_changed (GtkTreeModel *tree_model, - GtkTreePath *path, - GtkTreeIter *iter); -GDK_AVAILABLE_IN_ALL -void gtk_tree_model_row_inserted (GtkTreeModel *tree_model, - GtkTreePath *path, - GtkTreeIter *iter); -GDK_AVAILABLE_IN_ALL -void gtk_tree_model_row_has_child_toggled (GtkTreeModel *tree_model, - GtkTreePath *path, - GtkTreeIter *iter); -GDK_AVAILABLE_IN_ALL -void gtk_tree_model_row_deleted (GtkTreeModel *tree_model, - GtkTreePath *path); -GDK_AVAILABLE_IN_ALL -void gtk_tree_model_rows_reordered (GtkTreeModel *tree_model, - GtkTreePath *path, - GtkTreeIter *iter, - int *new_order); -GDK_AVAILABLE_IN_ALL -void gtk_tree_model_rows_reordered_with_length (GtkTreeModel *tree_model, - GtkTreePath *path, - GtkTreeIter *iter, - int *new_order, - int length); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkTreeModel, g_object_unref) -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkTreeIter, gtk_tree_iter_free) -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkTreePath, gtk_tree_path_free) -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkTreeRowReference, gtk_tree_row_reference_free) - -G_END_DECLS - -#endif /* __GTK_TREE_MODEL_H__ */ diff --git a/gtk/gtktreemodelfilter.c b/gtk/gtktreemodelfilter.c deleted file mode 100644 index cdad0ed111..0000000000 --- a/gtk/gtktreemodelfilter.c +++ /dev/null @@ -1,4261 +0,0 @@ -/* gtktreemodelfilter.c - * Copyright (C) 2000,2001 Red Hat, Inc., Jonathan Blandford - * Copyright (C) 2001-2003 Kristian Rietveld - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#include "config.h" -#include "gtktreemodelfilter.h" -#include "gtktreednd.h" -#include "gtkprivate.h" -#include - - -/** - * GtkTreeModelFilter: - * - * A `GtkTreeModel` which hides parts of an underlying tree model - * - * A `GtkTreeModelFilter` is a tree model which wraps another tree model, - * and can do the following things: - * - * - Filter specific rows, based on data from a “visible column”, a column - * storing booleans indicating whether the row should be filtered or not, - * or based on the return value of a “visible function”, which gets a - * model, iter and user_data and returns a boolean indicating whether the - * row should be filtered or not. - * - * - Modify the “appearance” of the model, using a modify function. - * This is extremely powerful and allows for just changing some - * values and also for creating a completely different model based - * on the given child model. - * - * - Set a different root node, also known as a “virtual root”. You can pass - * in a `GtkTreePath` indicating the root node for the filter at construction - * time. - * - * The basic API is similar to `GtkTreeModelSort`. For an example on its usage, - * see the section on `GtkTreeModelSort`. - * - * When using `GtkTreeModelFilter`, it is important to realize that - * `GtkTreeModelFilter` maintains an internal cache of all nodes which are - * visible in its clients. The cache is likely to be a subtree of the tree - * exposed by the child model. `GtkTreeModelFilter` will not cache the entire - * child model when unnecessary to not compromise the caching mechanism - * that is exposed by the reference counting scheme. If the child model - * implements reference counting, unnecessary signals may not be emitted - * because of reference counting rule 3, see the `GtkTreeModel` - * documentation. (Note that e.g. `GtkTreeStore` does not implement - * reference counting and will always emit all signals, even when - * the receiving node is not visible). - * - * Because of this, limitations for possible visible functions do apply. - * In general, visible functions should only use data or properties from - * the node for which the visibility state must be determined, its siblings - * or its parents. Usually, having a dependency on the state of any child - * node is not possible, unless references are taken on these explicitly. - * When no such reference exists, no signals may be received for these child - * nodes (see reference counting rule number 3 in the `GtkTreeModel` section). - * - * Determining the visibility state of a given node based on the state - * of its child nodes is a frequently occurring use case. Therefore, - * `GtkTreeModelFilter` explicitly supports this. For example, when a node - * does not have any children, you might not want the node to be visible. - * As soon as the first row is added to the node’s child level (or the - * last row removed), the node’s visibility should be updated. - * - * This introduces a dependency from the node on its child nodes. In order - * to accommodate this, `GtkTreeModelFilter` must make sure the necessary - * signals are received from the child model. This is achieved by building, - * for all nodes which are exposed as visible nodes to `GtkTreeModelFilter`'s - * clients, the child level (if any) and take a reference on the first node - * in this level. Furthermore, for every row-inserted, row-changed or - * row-deleted signal (also these which were not handled because the node - * was not cached), `GtkTreeModelFilter` will check if the visibility state - * of any parent node has changed. - * - * Beware, however, that this explicit support is limited to these two - * cases. For example, if you want a node to be visible only if two nodes - * in a child’s child level (2 levels deeper) are visible, you are on your - * own. In this case, either rely on `GtkTreeStore` to emit all signals - * because it does not implement reference counting, or for models that - * do implement reference counting, obtain references on these child levels - * yourself. - */ - -/* Notes on this implementation of GtkTreeModelFilter - * ================================================== - * - * Warnings - * -------- - * - * In this code there is a potential for confusion as to whether an iter, - * path or value refers to the GtkTreeModelFilter model, or to the child - * model that has been set. As a convention, variables referencing the - * child model will have a c_ prefix before them (ie. c_iter, c_value, - * c_path). In case the c_ prefixed names are already in use, an f_ - * prefix is used. Conversion of iterators and paths between - * GtkTreeModelFilter and the child model is done through the various - * gtk_tree_model_filter_convert_* functions. - * - * Even though the GtkTreeModelSort and GtkTreeModelFilter have very - * similar data structures, many assumptions made in the GtkTreeModelSort - * code do *not* apply in the GtkTreeModelFilter case. Reference counting - * in particular is more complicated in GtkTreeModelFilter, because - * we explicitly support reliance on the state of a node’s children as - * outlined in the public API documentation. Because of these differences, - * you are strongly recommended to first read through these notes before - * making any modification to the code. - * - * Iterator format - * --------------- - * - * The iterator format of iterators handed out by GtkTreeModelFilter is - * as follows: - * - * iter->stamp = filter->priv->stamp - * iter->user_data = FilterLevel - * iter->user_data2 = FilterElt - * - * Internal data structure - * ----------------------- - * - * Using FilterLevel and FilterElt, GtkTreeModelFilter maintains a “cache” - * of the mapping from GtkTreeModelFilter nodes to nodes in the child model. - * This is to avoid re-creating a level each time (which involves computing - * visibility for each node in that level) an operation is requested on - * GtkTreeModelFilter, such as get iter, get path and get value. - * - * A FilterElt corresponds to a single node. The FilterElt can either be - * visible or invisible in the model that is exposed to the clients of this - * GtkTreeModelFilter. The visibility state is stored in the “visible_siter” - * field, which is NULL when the node is not visible. The FilterLevel keeps - * a reference to the parent FilterElt and its FilterLevel (if any). The - * FilterElt can have a “children” pointer set, which points at a child - * level (a sub level). - * - * In a FilterLevel, two separate GSequences are maintained. One contains - * all nodes of this FilterLevel, regardless of the visibility state of - * the node. Another contains only visible nodes. A visible FilterElt - * is thus present in both the full and the visible GSequence. The - * GSequence allows for fast access, addition and removal of nodes. - * - * It is important to recognize the two different mappings that play - * a part in this code: - * I. The mapping from the client to this model. The order in which - * nodes are stored in the *visible* GSequence is the order in - * which the nodes are exposed to clients of the GtkTreeModelFilter. - * II. The mapping from this model to its child model. Each FilterElt - * contains an “offset” field which is the offset of the - * corresponding node in the child model. - * - * Throughout the code, two kinds of paths relative to the GtkTreeModelFilter - * (those generated from the sequence positions) are used. There are paths - * which take non-visible nodes into account (generated from the full - * sequences) and paths which don’t (generated from the visible sequences). - * Paths which have been generated from the full sequences should only be - * used internally and NEVER be passed along with a signal emisson. - * - * Reference counting - * ------------------ - * - * GtkTreeModelFilter forwards all reference and unreference operations - * to the corresponding node in the child model. In addition, - * GtkTreeModelFilter will also add references of its own. The full reference - * count of each node (i.e. all forwarded references and these by the - * filter model) is maintained internally in the “ref_count” fields in - * FilterElt and FilterLevel. Because there is a need to determine whether - * a node should be visible for the client, the reference count of only - * the forwarded references is maintained as well, in the “ext_ref_count” - * fields. - * - * In a few cases, GtkTreeModelFilter takes additional references on - * nodes. The first case is that a reference is taken on the parent - * (if any) of each level. This happens in gtk_tree_model_filter_build_level() - * and the reference is released again in gtk_tree_model_filter_free_level(). - * This ensures that for all references which are taken by the filter - * model, all parent nodes are referenced according to reference counting - * rule 1 in the GtkTreeModel documentation. - * - * A second case is required to support visible functions which depend on - * the state of a node’s children (see the public API documentation for - * GtkTreeModelFilter above). We build the child level of each node that - * could be visible in the client (i.e. the level has an ext_ref_count > 0; - * not the elt, because the elt might be invisible and thus unreferenced - * by the client). For each node that becomes visible, due to insertion or - * changes in visibility state, it is checked whether node has children, if - * so the child level is built. - * - * A reference is taken on the first node of each level so that the child - * model will emit all signals for this level, due to reference counting - * rule 3 in the GtkTreeModel documentation. If due to changes in the level, - * another node becomes the first node (e.g. due to insertion or reordering), - * this reference is transferred from the old to the new first node. - * - * When a level has an *external* reference count of zero (which means that - * none of the nodes in the level is referenced by the clients), the level - * has a “zero ref count” on all its parents. As soon as the level reaches - * an *external* reference count of zero, the zero ref count value is - * incremented by one for all parents of this level. Due to the additional - * references taken by the filter model, it is important to base the - * zero ref count on the external reference count instead of on the full - * reference count of the node. - * - * The zero ref count value aids in determining which portions of the - * cache are possibly unused and could be removed. If a FilterElt has - * a zero ref count of one, then its child level is unused. However, the - * child level can only be removed from the cache if the FilterElt's - * parent level has an external ref count of zero. (Not the parent elt, - * because an invisible parent elt with external ref count == 0 might still - * become visible because of a state change in its child level!). Otherwise, - * monitoring this level is necessary to possibly update the visibility state - * of the parent. This is an important difference from GtkTreeModelSort! - * - * Signals are only required for levels with an external ref count > 0. - * This due to reference counting rule 3, see the GtkTreeModel - * documentation. In the GtkTreeModelFilter we try hard to stick to this - * rule and not emit redundant signals (though redundant emissions of - * row-has-child-toggled could appear frequently; it does happen that - * we simply forward the signal emitted by e.g. GtkTreeStore but also - * emit our own copy). - */ - - -typedef struct _FilterElt FilterElt; -typedef struct _FilterLevel FilterLevel; - -struct _FilterElt -{ - GtkTreeIter iter; - FilterLevel *children; - int offset; - int ref_count; - int ext_ref_count; - int zero_ref_count; - GSequenceIter *visible_siter; /* iter into visible_seq */ -}; - -struct _FilterLevel -{ - GSequence *seq; - GSequence *visible_seq; - int ref_count; - int ext_ref_count; - - FilterElt *parent_elt; - FilterLevel *parent_level; -}; - - -struct _GtkTreeModelFilterPrivate -{ - GtkTreeModel *child_model; - gpointer root; - GtkTreePath *virtual_root; - - int stamp; - guint child_flags; - int zero_ref_count; - int visible_column; - - GtkTreeModelFilterVisibleFunc visible_func; - gpointer visible_data; - GDestroyNotify visible_destroy; - - GType *modify_types; - GtkTreeModelFilterModifyFunc modify_func; - gpointer modify_data; - GDestroyNotify modify_destroy; - int modify_n_columns; - - guint visible_method_set : 1; - guint modify_func_set : 1; - - guint in_row_deleted : 1; - guint virtual_root_deleted : 1; - - /* signal ids */ - gulong changed_id; - gulong inserted_id; - gulong has_child_toggled_id; - gulong deleted_id; - gulong reordered_id; -}; - -/* properties */ -enum -{ - PROP_0, - PROP_CHILD_MODEL, - PROP_VIRTUAL_ROOT -}; - -/* Set this to 0 to disable caching of child iterators. This - * allows for more stringent testing. It is recommended to set this - * to one when refactoring this code and running the unit tests to - * catch more errors. - */ -#if 1 -# define GTK_TREE_MODEL_FILTER_CACHE_CHILD_ITERS(filter) \ - (((GtkTreeModelFilter *)filter)->priv->child_flags & GTK_TREE_MODEL_ITERS_PERSIST) -#else -# define GTK_TREE_MODEL_FILTER_CACHE_CHILD_ITERS(filter) (FALSE) -#endif - -/* Defining this constant enables more assertions, which will be - * helpful when debugging the code. - */ -#undef MODEL_FILTER_DEBUG - -#define FILTER_ELT(filter_elt) ((FilterElt *)filter_elt) -#define FILTER_LEVEL(filter_level) ((FilterLevel *)filter_level) -#define GET_ELT(siter) ((FilterElt*) (siter ? g_sequence_get (siter) : NULL)) - -/* general code (object/interface init, properties, etc) */ -static void gtk_tree_model_filter_tree_model_init (GtkTreeModelIface *iface); -static void gtk_tree_model_filter_drag_source_init (GtkTreeDragSourceIface *iface); -static void gtk_tree_model_filter_finalize (GObject *object); -static void gtk_tree_model_filter_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec); -static void gtk_tree_model_filter_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec); - -/* signal handlers */ -static void gtk_tree_model_filter_row_changed (GtkTreeModel *c_model, - GtkTreePath *c_path, - GtkTreeIter *c_iter, - gpointer data); -static void gtk_tree_model_filter_row_inserted (GtkTreeModel *c_model, - GtkTreePath *c_path, - GtkTreeIter *c_iter, - gpointer data); -static void gtk_tree_model_filter_row_has_child_toggled (GtkTreeModel *c_model, - GtkTreePath *c_path, - GtkTreeIter *c_iter, - gpointer data); -static void gtk_tree_model_filter_row_deleted (GtkTreeModel *c_model, - GtkTreePath *c_path, - gpointer data); -static void gtk_tree_model_filter_rows_reordered (GtkTreeModel *c_model, - GtkTreePath *c_path, - GtkTreeIter *c_iter, - int *new_order, - gpointer data); - -/* GtkTreeModel interface */ -static GtkTreeModelFlags gtk_tree_model_filter_get_flags (GtkTreeModel *model); -static int gtk_tree_model_filter_get_n_columns (GtkTreeModel *model); -static GType gtk_tree_model_filter_get_column_type (GtkTreeModel *model, - int index); -static gboolean gtk_tree_model_filter_get_iter_full (GtkTreeModel *model, - GtkTreeIter *iter, - GtkTreePath *path); -static gboolean gtk_tree_model_filter_get_iter (GtkTreeModel *model, - GtkTreeIter *iter, - GtkTreePath *path); -static GtkTreePath *gtk_tree_model_filter_get_path (GtkTreeModel *model, - GtkTreeIter *iter); -static void gtk_tree_model_filter_get_value (GtkTreeModel *model, - GtkTreeIter *iter, - int column, - GValue *value); -static gboolean gtk_tree_model_filter_iter_next (GtkTreeModel *model, - GtkTreeIter *iter); -static gboolean gtk_tree_model_filter_iter_previous (GtkTreeModel *model, - GtkTreeIter *iter); -static gboolean gtk_tree_model_filter_iter_children (GtkTreeModel *model, - GtkTreeIter *iter, - GtkTreeIter *parent); -static gboolean gtk_tree_model_filter_iter_has_child (GtkTreeModel *model, - GtkTreeIter *iter); -static int gtk_tree_model_filter_iter_n_children (GtkTreeModel *model, - GtkTreeIter *iter); -static gboolean gtk_tree_model_filter_iter_nth_child (GtkTreeModel *model, - GtkTreeIter *iter, - GtkTreeIter *parent, - int n); -static gboolean gtk_tree_model_filter_iter_parent (GtkTreeModel *model, - GtkTreeIter *iter, - GtkTreeIter *child); -static void gtk_tree_model_filter_ref_node (GtkTreeModel *model, - GtkTreeIter *iter); -static void gtk_tree_model_filter_unref_node (GtkTreeModel *model, - GtkTreeIter *iter); - -/* TreeDragSource interface */ -static gboolean gtk_tree_model_filter_row_draggable (GtkTreeDragSource *drag_source, - GtkTreePath *path); -static GdkContentProvider * - gtk_tree_model_filter_drag_data_get (GtkTreeDragSource *drag_source, - GtkTreePath *path); -static gboolean gtk_tree_model_filter_drag_data_delete (GtkTreeDragSource *drag_source, - GtkTreePath *path); - -/* private functions */ -static void gtk_tree_model_filter_build_level (GtkTreeModelFilter *filter, - FilterLevel *parent_level, - FilterElt *parent_elt, - gboolean emit_inserted); - -static void gtk_tree_model_filter_free_level (GtkTreeModelFilter *filter, - FilterLevel *filter_level, - gboolean unref_self, - gboolean unref_parent, - gboolean unref_external); - -static GtkTreePath *gtk_tree_model_filter_elt_get_path (FilterLevel *level, - FilterElt *elt, - GtkTreePath *root); - -static GtkTreePath *gtk_tree_model_filter_add_root (GtkTreePath *src, - GtkTreePath *root); -static GtkTreePath *gtk_tree_model_filter_remove_root (GtkTreePath *src, - GtkTreePath *root); - -static void gtk_tree_model_filter_increment_stamp (GtkTreeModelFilter *filter); - -static void gtk_tree_model_filter_real_modify (GtkTreeModelFilter *self, - GtkTreeModel *child_model, - GtkTreeIter *iter, - GValue *value, - int column); -static gboolean gtk_tree_model_filter_real_visible (GtkTreeModelFilter *filter, - GtkTreeModel *child_model, - GtkTreeIter *child_iter); -static gboolean gtk_tree_model_filter_visible (GtkTreeModelFilter *filter, - GtkTreeIter *child_iter); -static void gtk_tree_model_filter_clear_cache_helper (GtkTreeModelFilter *filter, - FilterLevel *level); - -static void gtk_tree_model_filter_real_ref_node (GtkTreeModel *model, - GtkTreeIter *iter, - gboolean external); -static void gtk_tree_model_filter_real_unref_node (GtkTreeModel *model, - GtkTreeIter *iter, - gboolean external, - gboolean propagate_unref); - -static void gtk_tree_model_filter_set_model (GtkTreeModelFilter *filter, - GtkTreeModel *child_model); -static void gtk_tree_model_filter_ref_path (GtkTreeModelFilter *filter, - GtkTreePath *path); -static void gtk_tree_model_filter_unref_path (GtkTreeModelFilter *filter, - GtkTreePath *path, - int depth); -static void gtk_tree_model_filter_set_root (GtkTreeModelFilter *filter, - GtkTreePath *root); - -static GtkTreePath *gtk_real_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter *filter, - GtkTreePath *child_path, - gboolean build_levels, - gboolean fetch_children); - -static gboolean gtk_tree_model_filter_elt_is_visible_in_target (FilterLevel *level, - FilterElt *elt); - -static FilterElt *gtk_tree_model_filter_insert_elt_in_level (GtkTreeModelFilter *filter, - GtkTreeIter *c_iter, - FilterLevel *level, - int offset, - int *index); -static FilterElt *gtk_tree_model_filter_fetch_child (GtkTreeModelFilter *filter, - FilterLevel *level, - int offset, - int *index); -static void gtk_tree_model_filter_remove_elt_from_level (GtkTreeModelFilter *filter, - FilterLevel *level, - FilterElt *elt); -static void gtk_tree_model_filter_update_children (GtkTreeModelFilter *filter, - FilterLevel *level, - FilterElt *elt); -static void gtk_tree_model_filter_emit_row_inserted_for_path (GtkTreeModelFilter *filter, - GtkTreeModel *c_model, - GtkTreePath *c_path, - GtkTreeIter *c_iter); - - -G_DEFINE_TYPE_WITH_CODE (GtkTreeModelFilter, gtk_tree_model_filter, G_TYPE_OBJECT, - G_ADD_PRIVATE (GtkTreeModelFilter) - G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, - gtk_tree_model_filter_tree_model_init) - G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE, - gtk_tree_model_filter_drag_source_init)) - -static void -gtk_tree_model_filter_init (GtkTreeModelFilter *filter) -{ - filter->priv = gtk_tree_model_filter_get_instance_private (filter); - filter->priv->visible_column = -1; - filter->priv->zero_ref_count = 0; - filter->priv->visible_method_set = FALSE; - filter->priv->modify_func_set = FALSE; - filter->priv->in_row_deleted = FALSE; - filter->priv->virtual_root_deleted = FALSE; -} - -static void -gtk_tree_model_filter_class_init (GtkTreeModelFilterClass *filter_class) -{ - GObjectClass *object_class; - - object_class = (GObjectClass *) filter_class; - - object_class->set_property = gtk_tree_model_filter_set_property; - object_class->get_property = gtk_tree_model_filter_get_property; - - object_class->finalize = gtk_tree_model_filter_finalize; - - filter_class->visible = gtk_tree_model_filter_real_visible; - filter_class->modify = gtk_tree_model_filter_real_modify; - - g_object_class_install_property (object_class, - PROP_CHILD_MODEL, - g_param_spec_object ("child-model", NULL, NULL, - GTK_TYPE_TREE_MODEL, - GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property (object_class, - PROP_VIRTUAL_ROOT, - g_param_spec_boxed ("virtual-root", NULL, NULL, - GTK_TYPE_TREE_PATH, - GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); -} - -static void -gtk_tree_model_filter_tree_model_init (GtkTreeModelIface *iface) -{ - iface->get_flags = gtk_tree_model_filter_get_flags; - iface->get_n_columns = gtk_tree_model_filter_get_n_columns; - iface->get_column_type = gtk_tree_model_filter_get_column_type; - iface->get_iter = gtk_tree_model_filter_get_iter; - iface->get_path = gtk_tree_model_filter_get_path; - iface->get_value = gtk_tree_model_filter_get_value; - iface->iter_next = gtk_tree_model_filter_iter_next; - iface->iter_previous = gtk_tree_model_filter_iter_previous; - iface->iter_children = gtk_tree_model_filter_iter_children; - iface->iter_has_child = gtk_tree_model_filter_iter_has_child; - iface->iter_n_children = gtk_tree_model_filter_iter_n_children; - iface->iter_nth_child = gtk_tree_model_filter_iter_nth_child; - iface->iter_parent = gtk_tree_model_filter_iter_parent; - iface->ref_node = gtk_tree_model_filter_ref_node; - iface->unref_node = gtk_tree_model_filter_unref_node; -} - -static void -gtk_tree_model_filter_drag_source_init (GtkTreeDragSourceIface *iface) -{ - iface->row_draggable = gtk_tree_model_filter_row_draggable; - iface->drag_data_delete = gtk_tree_model_filter_drag_data_delete; - iface->drag_data_get = gtk_tree_model_filter_drag_data_get; -} - - -static void -gtk_tree_model_filter_finalize (GObject *object) -{ - GtkTreeModelFilter *filter = (GtkTreeModelFilter *) object; - - if (filter->priv->virtual_root && !filter->priv->virtual_root_deleted) - { - gtk_tree_model_filter_unref_path (filter, filter->priv->virtual_root, - -1); - filter->priv->virtual_root_deleted = TRUE; - } - - gtk_tree_model_filter_set_model (filter, NULL); - - if (filter->priv->virtual_root) - gtk_tree_path_free (filter->priv->virtual_root); - - if (filter->priv->root) - gtk_tree_model_filter_free_level (filter, filter->priv->root, TRUE, TRUE, FALSE); - - g_free (filter->priv->modify_types); - - if (filter->priv->modify_destroy) - filter->priv->modify_destroy (filter->priv->modify_data); - - if (filter->priv->visible_destroy) - filter->priv->visible_destroy (filter->priv->visible_data); - - /* must chain up */ - G_OBJECT_CLASS (gtk_tree_model_filter_parent_class)->finalize (object); -} - -static void -gtk_tree_model_filter_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (object); - - switch (prop_id) - { - case PROP_CHILD_MODEL: - gtk_tree_model_filter_set_model (filter, g_value_get_object (value)); - break; - case PROP_VIRTUAL_ROOT: - gtk_tree_model_filter_set_root (filter, g_value_get_boxed (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gtk_tree_model_filter_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (object); - - switch (prop_id) - { - case PROP_CHILD_MODEL: - g_value_set_object (value, filter->priv->child_model); - break; - case PROP_VIRTUAL_ROOT: - g_value_set_boxed (value, filter->priv->virtual_root); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -/* helpers */ - -static FilterElt * -filter_elt_new (void) -{ - return g_slice_new (FilterElt); -} - -static void -filter_elt_free (gpointer elt) -{ - g_slice_free (FilterElt, elt); -} - -static int -filter_elt_cmp (gconstpointer a, - gconstpointer b, - gpointer user_data) -{ - const FilterElt *elt_a = a; - const FilterElt *elt_b = b; - - if (elt_a->offset > elt_b->offset) - return +1; - else if (elt_a->offset < elt_b->offset) - return -1; - else - return 0; -} - -static FilterElt * -lookup_elt_with_offset (GSequence *seq, - int offset, - GSequenceIter **ret_siter) -{ - GSequenceIter *siter; - FilterElt dummy; - - dummy.offset = offset; - siter = g_sequence_lookup (seq, &dummy, filter_elt_cmp, NULL); - - if (ret_siter) - *ret_siter = siter; - - return GET_ELT (siter); -} - -static void -increase_offset_iter (gpointer data, - gpointer user_data) -{ - FilterElt *elt = data; - int offset = GPOINTER_TO_INT (user_data); - - if (elt->offset >= offset) - elt->offset++; -} - -static void -decrease_offset_iter (gpointer data, - gpointer user_data) -{ - FilterElt *elt = data; - int offset = GPOINTER_TO_INT (user_data); - - if (elt->offset > offset) - elt->offset--; -} - -static void -gtk_tree_model_filter_build_level (GtkTreeModelFilter *filter, - FilterLevel *parent_level, - FilterElt *parent_elt, - gboolean emit_inserted) -{ - GtkTreeIter iter; - GtkTreeIter first_node; - GtkTreeIter root; - FilterLevel *new_level; - FilterLevel *tmp_level; - FilterElt *tmp_elt; - GtkTreeIter f_iter; - int length = 0; - int i; - gboolean empty = TRUE; - - g_assert (filter->priv->child_model != NULL); - - /* Avoid building a level that already exists */ - if (parent_level) - g_assert (parent_elt->children == NULL); - else - g_assert (filter->priv->root == NULL); - - if (filter->priv->in_row_deleted) - return; - - if (!parent_level) - { - if (filter->priv->virtual_root) - { - if (gtk_tree_model_get_iter (filter->priv->child_model, &root, filter->priv->virtual_root) == FALSE) - return; - length = gtk_tree_model_iter_n_children (filter->priv->child_model, &root); - - if (gtk_tree_model_iter_children (filter->priv->child_model, &iter, &root) == FALSE) - return; - } - else - { - if (!gtk_tree_model_get_iter_first (filter->priv->child_model, &iter)) - return; - length = gtk_tree_model_iter_n_children (filter->priv->child_model, NULL); - } - } - else - { - GtkTreeIter parent_iter; - GtkTreeIter child_parent_iter; - - parent_iter.stamp = filter->priv->stamp; - parent_iter.user_data = parent_level; - parent_iter.user_data2 = parent_elt; - - gtk_tree_model_filter_convert_iter_to_child_iter (filter, - &child_parent_iter, - &parent_iter); - if (gtk_tree_model_iter_children (filter->priv->child_model, &iter, &child_parent_iter) == FALSE) - return; - - /* stamp may have changed */ - gtk_tree_model_filter_convert_iter_to_child_iter (filter, - &child_parent_iter, - &parent_iter); - length = gtk_tree_model_iter_n_children (filter->priv->child_model, &child_parent_iter); - - /* Take a reference on the parent */ - gtk_tree_model_filter_real_ref_node (GTK_TREE_MODEL (filter), - &parent_iter, FALSE); - } - - g_return_if_fail (length > 0); - - new_level = g_new (FilterLevel, 1); - new_level->seq = g_sequence_new (filter_elt_free); - new_level->visible_seq = g_sequence_new (NULL); - new_level->ref_count = 0; - new_level->ext_ref_count = 0; - new_level->parent_elt = parent_elt; - new_level->parent_level = parent_level; - - if (parent_elt) - parent_elt->children = new_level; - else - filter->priv->root = new_level; - - /* increase the count of zero ref_counts */ - tmp_level = parent_level; - tmp_elt = parent_elt; - - while (tmp_level) - { - tmp_elt->zero_ref_count++; - - tmp_elt = tmp_level->parent_elt; - tmp_level = tmp_level->parent_level; - } - if (new_level != filter->priv->root) - filter->priv->zero_ref_count++; - - i = 0; - - first_node = iter; - - do - { - if (gtk_tree_model_filter_visible (filter, &iter)) - { - FilterElt *filter_elt; - - filter_elt = filter_elt_new (); - filter_elt->offset = i; - filter_elt->zero_ref_count = 0; - filter_elt->ref_count = 0; - filter_elt->ext_ref_count = 0; - filter_elt->children = NULL; - filter_elt->visible_siter = NULL; - - if (GTK_TREE_MODEL_FILTER_CACHE_CHILD_ITERS (filter)) - filter_elt->iter = iter; - - g_sequence_append (new_level->seq, filter_elt); - filter_elt->visible_siter = g_sequence_append (new_level->visible_seq, filter_elt); - empty = FALSE; - - if (emit_inserted) - { - GtkTreePath *f_path; - GtkTreeIter children; - - f_iter.stamp = filter->priv->stamp; - f_iter.user_data = new_level; - f_iter.user_data2 = filter_elt; - - f_path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), - &f_iter); - gtk_tree_model_row_inserted (GTK_TREE_MODEL (filter), - f_path, &f_iter); - gtk_tree_path_free (f_path); - - if (gtk_tree_model_iter_children (filter->priv->child_model, - &children, &iter)) - gtk_tree_model_filter_update_children (filter, - new_level, - FILTER_ELT (f_iter.user_data2)); - } - } - i++; - } - while (gtk_tree_model_iter_next (filter->priv->child_model, &iter)); - - /* The level does not contain any visible nodes. However, changes in - * this level might affect the parent node, which can either be visible - * or invisible. Therefore, this level can only be removed again, - * if the parent level has an external reference count of zero. That is, - * if this level changes state, no signals are required in the parent - * level. - */ - if (empty && - (parent_level && parent_level->ext_ref_count == 0)) - { - gtk_tree_model_filter_free_level (filter, new_level, FALSE, TRUE, FALSE); - return; - } - - /* If none of the nodes are visible, we will just pull in the - * first node of the level. - */ - if (empty) - { - FilterElt *filter_elt; - - filter_elt = filter_elt_new (); - filter_elt->offset = 0; - filter_elt->zero_ref_count = 0; - filter_elt->ref_count = 0; - filter_elt->ext_ref_count = 0; - filter_elt->children = NULL; - filter_elt->visible_siter = NULL; - - if (GTK_TREE_MODEL_FILTER_CACHE_CHILD_ITERS (filter)) - filter_elt->iter = first_node; - - g_sequence_append (new_level->seq, filter_elt); - } - - /* Keep a reference on the first node of this level. We need this - * to make sure that we get all signals for this level. - */ - f_iter.stamp = filter->priv->stamp; - f_iter.user_data = new_level; - f_iter.user_data2 = g_sequence_get (g_sequence_get_begin_iter (new_level->seq)); - - gtk_tree_model_filter_real_ref_node (GTK_TREE_MODEL (filter), &f_iter, FALSE); -} - -static void -gtk_tree_model_filter_free_level (GtkTreeModelFilter *filter, - FilterLevel *filter_level, - gboolean unref_self, - gboolean unref_parent, - gboolean unref_external) -{ - GSequenceIter *siter; - GSequenceIter *end_siter; - - g_assert (filter_level); - - end_siter = g_sequence_get_end_iter (filter_level->seq); - for (siter = g_sequence_get_begin_iter (filter_level->seq); - siter != end_siter; - siter = g_sequence_iter_next (siter)) - { - FilterElt *elt = g_sequence_get (siter); - - if (elt->children) - { - /* If we recurse and unref_self == FALSE, then unref_parent - * must also be FALSE (otherwise a still unref a node in this - * level). - */ - gtk_tree_model_filter_free_level (filter, - FILTER_LEVEL (elt->children), - unref_self, - unref_self == FALSE ? FALSE : unref_parent, - unref_external); - } - - if (unref_external) - { - GtkTreeIter f_iter; - - f_iter.stamp = filter->priv->stamp; - f_iter.user_data = filter_level; - f_iter.user_data2 = elt; - - while (elt->ext_ref_count > 0) - gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter), - &f_iter, - TRUE, unref_self); - } - } - - /* Release the reference on the first item. - */ - if (unref_self) - { - GtkTreeIter f_iter; - - f_iter.stamp = filter->priv->stamp; - f_iter.user_data = filter_level; - f_iter.user_data2 = g_sequence_get (g_sequence_get_begin_iter (filter_level->seq)); - - gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter), - &f_iter, FALSE, TRUE); - } - - if (filter_level->ext_ref_count == 0) - { - FilterLevel *parent_level = filter_level->parent_level; - FilterElt *parent_elt = filter_level->parent_elt; - - while (parent_level) - { - parent_elt->zero_ref_count--; - - parent_elt = parent_level->parent_elt; - parent_level = parent_level->parent_level; - } - - if (filter_level != filter->priv->root) - filter->priv->zero_ref_count--; - } - -#ifdef MODEL_FILTER_DEBUG - if (filter_level == filter->priv->root) - g_assert (filter->priv->zero_ref_count == 0); -#endif - - if (filter_level->parent_elt) - { - /* Release reference on parent */ - GtkTreeIter parent_iter; - - parent_iter.stamp = filter->priv->stamp; - parent_iter.user_data = filter_level->parent_level; - parent_iter.user_data2 = filter_level->parent_elt; - - gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter), - &parent_iter, FALSE, unref_parent); - - filter_level->parent_elt->children = NULL; - } - else - filter->priv->root = NULL; - - g_sequence_free (filter_level->seq); - g_sequence_free (filter_level->visible_seq); - g_free (filter_level); -} - -/* prune_level() is like free_level(), however instead of being fully - * freed, the level is pruned to a level with only the first node used - * for monitoring. For now it is only being called from - * gtk_tree_model_filter_remove_elt_from_level(), which is the reason - * this function is lacking a “gboolean unref” argument. - */ -static void -gtk_tree_model_filter_prune_level (GtkTreeModelFilter *filter, - FilterLevel *level) -{ - GSequenceIter *siter; - GSequenceIter *end_siter; - FilterElt *elt; - GtkTreeIter f_iter; - - /* This function is called when the parent of level became invisible. - * All external ref counts of the children need to be dropped. - * All children except the first one can be removed. - */ - - /* Any child levels can be freed */ - end_siter = g_sequence_get_end_iter (level->seq); - for (siter = g_sequence_get_begin_iter (level->seq); - siter != end_siter; - siter = g_sequence_iter_next (siter)) - { - elt = g_sequence_get (siter); - - if (elt->children) - gtk_tree_model_filter_free_level (filter, - FILTER_LEVEL (elt->children), - TRUE, TRUE, TRUE); - } - - /* For the first item, only drop the external references */ - elt = g_sequence_get (g_sequence_get_begin_iter (level->seq)); - - f_iter.stamp = filter->priv->stamp; - f_iter.user_data = level; - f_iter.user_data2 = elt; - - while (elt->ext_ref_count > 0) - gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter), - &f_iter, TRUE, TRUE); - - if (elt->visible_siter) - { - g_sequence_remove (elt->visible_siter); - elt->visible_siter = NULL; - } - - /* Remove the other elts */ - end_siter = g_sequence_get_end_iter (level->seq); - siter = g_sequence_get_begin_iter (level->seq); - siter = g_sequence_iter_next (siter); - for (; siter != end_siter; siter = g_sequence_iter_next (siter)) - { - elt = g_sequence_get (siter); - - f_iter.stamp = filter->priv->stamp; - f_iter.user_data = level; - f_iter.user_data2 = elt; - - while (elt->ext_ref_count > 0) - gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter), - &f_iter, TRUE, TRUE); - /* In this case, we do remove reference counts we've added ourselves, - * since the node will be removed from the data structures. - */ - while (elt->ref_count > 0) - gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter), - &f_iter, FALSE, TRUE); - - if (elt->visible_siter) - { - g_sequence_remove (elt->visible_siter); - elt->visible_siter = NULL; - } - } - - /* Remove [begin + 1, end] */ - siter = g_sequence_get_begin_iter (level->seq); - siter = g_sequence_iter_next (siter); - - g_sequence_remove_range (siter, end_siter); - - /* The level must have reached an ext ref count of zero by now, though - * we only assert on this in debugging mode. - */ -#ifdef MODEL_FILTER_DEBUG - g_assert (level->ext_ref_count == 0); -#endif -} - -static void -gtk_tree_model_filter_level_transfer_first_ref (GtkTreeModelFilter *filter, - FilterLevel *level, - GSequenceIter *from_iter, - GSequenceIter *to_iter) -{ - GtkTreeIter f_iter; - - f_iter.stamp = filter->priv->stamp; - f_iter.user_data = level; - f_iter.user_data2 = g_sequence_get (to_iter); - - gtk_tree_model_filter_real_ref_node (GTK_TREE_MODEL (filter), - &f_iter, FALSE); - - f_iter.stamp = filter->priv->stamp; - f_iter.user_data = level; - f_iter.user_data2 = g_sequence_get (from_iter); - - gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter), - &f_iter, FALSE, TRUE); -} - -static void -gtk_tree_model_filter_level_transfer_first_ref_with_index (GtkTreeModelFilter *filter, - FilterLevel *level, - int from_index, - int to_index) -{ - gtk_tree_model_filter_level_transfer_first_ref (filter, level, - g_sequence_get_iter_at_pos (level->seq, from_index), - g_sequence_get_iter_at_pos (level->seq, to_index)); -} - -/* Creates paths suitable for accessing the child model. */ -static GtkTreePath * -gtk_tree_model_filter_elt_get_path (FilterLevel *level, - FilterElt *elt, - GtkTreePath *root) -{ - FilterLevel *walker = level; - FilterElt *walker2 = elt; - GtkTreePath *path; - GtkTreePath *real_path; - - g_return_val_if_fail (level != NULL, NULL); - g_return_val_if_fail (elt != NULL, NULL); - - path = gtk_tree_path_new (); - - while (walker) - { - gtk_tree_path_prepend_index (path, walker2->offset); - - walker2 = walker->parent_elt; - walker = walker->parent_level; - } - - if (root) - { - real_path = gtk_tree_model_filter_add_root (path, root); - gtk_tree_path_free (path); - return real_path; - } - - return path; -} - -static GtkTreePath * -gtk_tree_model_filter_add_root (GtkTreePath *src, - GtkTreePath *root) -{ - GtkTreePath *retval; - int i; - - retval = gtk_tree_path_copy (root); - - for (i = 0; i < gtk_tree_path_get_depth (src); i++) - gtk_tree_path_append_index (retval, gtk_tree_path_get_indices (src)[i]); - - return retval; -} - -static GtkTreePath * -gtk_tree_model_filter_remove_root (GtkTreePath *src, - GtkTreePath *root) -{ - GtkTreePath *retval; - int i; - int depth; - int *indices; - - if (gtk_tree_path_get_depth (src) <= gtk_tree_path_get_depth (root)) - return NULL; - - depth = gtk_tree_path_get_depth (src); - indices = gtk_tree_path_get_indices (src); - - for (i = 0; i < gtk_tree_path_get_depth (root); i++) - if (indices[i] != gtk_tree_path_get_indices (root)[i]) - return NULL; - - retval = gtk_tree_path_new (); - - for (; i < depth; i++) - gtk_tree_path_append_index (retval, indices[i]); - - return retval; -} - -static void -gtk_tree_model_filter_increment_stamp (GtkTreeModelFilter *filter) -{ - do - { - filter->priv->stamp++; - } - while (filter->priv->stamp == 0); - - gtk_tree_model_filter_clear_cache (filter); -} - -static gboolean -gtk_tree_model_filter_real_visible (GtkTreeModelFilter *filter, - GtkTreeModel *child_model, - GtkTreeIter *child_iter) -{ - if (filter->priv->visible_func) - { - return filter->priv->visible_func (child_model, - child_iter, - filter->priv->visible_data) - ? TRUE : FALSE; - } - else if (filter->priv->visible_column >= 0) - { - GValue val = G_VALUE_INIT; - - gtk_tree_model_get_value (child_model, child_iter, - filter->priv->visible_column, &val); - - if (g_value_get_boolean (&val)) - { - g_value_unset (&val); - return TRUE; - } - - g_value_unset (&val); - return FALSE; - } - - /* no visible function set, so always visible */ - return TRUE; -} - -static gboolean -gtk_tree_model_filter_visible (GtkTreeModelFilter *self, - GtkTreeIter *child_iter) -{ - return GTK_TREE_MODEL_FILTER_GET_CLASS (self)->visible (self, - self->priv->child_model, child_iter); -} - -static void -gtk_tree_model_filter_clear_cache_helper_iter (gpointer data, - gpointer user_data) -{ - GtkTreeModelFilter *filter = user_data; - FilterElt *elt = data; - -#ifdef MODEL_FILTER_DEBUG - g_assert (elt->zero_ref_count >= 0); -#endif - - if (elt->zero_ref_count > 0) - gtk_tree_model_filter_clear_cache_helper (filter, elt->children); -} - -static void -gtk_tree_model_filter_clear_cache_helper (GtkTreeModelFilter *filter, - FilterLevel *level) -{ - g_assert (level); - - g_sequence_foreach (level->seq, gtk_tree_model_filter_clear_cache_helper_iter, filter); - - /* If the level's ext_ref_count is zero, it means the level is not visible - * and can be removed. But, since we support monitoring a child level - * of a parent for changes (these might affect the parent), we will only - * free the level if the parent level also has an external ref - * count of zero. In that case, changes concerning our parent are - * not requested. - * - * The root level is always visible, so an exception holds for levels - * with the root level as parent level: these have to remain cached. - */ - if (level->ext_ref_count == 0 && level != filter->priv->root && - level->parent_level && level->parent_level != filter->priv->root && - level->parent_level->ext_ref_count == 0) - { - gtk_tree_model_filter_free_level (filter, level, TRUE, TRUE, FALSE); - return; - } -} - -static gboolean -gtk_tree_model_filter_elt_is_visible_in_target (FilterLevel *level, - FilterElt *elt) -{ - if (!elt->visible_siter) - return FALSE; - - if (!level->parent_elt) - return TRUE; - - do - { - elt = level->parent_elt; - level = level->parent_level; - - if (elt && !elt->visible_siter) - return FALSE; - } - while (level); - - return TRUE; -} - -/* If a change has occurred in path (inserted, changed or deleted), - * then this function is used to check all its ancestors. An ancestor - * could have changed state as a result and this needs to be propagated - * to the objects monitoring the filter model. - */ -static void -gtk_tree_model_filter_check_ancestors (GtkTreeModelFilter *filter, - GtkTreePath *path) -{ - int i = 0; - int *indices = gtk_tree_path_get_indices (path); - FilterElt *elt; - FilterLevel *level; - GtkTreeIter c_iter, tmp_iter, *root_iter; - - level = FILTER_LEVEL (filter->priv->root); - - if (!level) - return; - - root_iter = NULL; - if (filter->priv->virtual_root && - gtk_tree_model_get_iter (filter->priv->child_model, &tmp_iter, - filter->priv->virtual_root)) - root_iter = &tmp_iter; - gtk_tree_model_iter_nth_child (filter->priv->child_model, &c_iter, - root_iter, - indices[i]); - - while (i < gtk_tree_path_get_depth (path) - 1) - { - gboolean requested_state; - - elt = lookup_elt_with_offset (level->seq, - gtk_tree_path_get_indices (path)[i], NULL); - - requested_state = gtk_tree_model_filter_visible (filter, &c_iter); - - if (!elt) - { - int index; - GtkTreePath *c_path; - - if (requested_state == FALSE) - return; - - /* The elt does not exist in this level (so it is not - * visible), but should now be visible. We emit the - * row-inserted and row-has-child-toggled signals. - */ - elt = gtk_tree_model_filter_insert_elt_in_level (filter, - &c_iter, - level, - indices[i], - &index); - - /* insert_elt_in_level defaults to FALSE */ - elt->visible_siter = g_sequence_insert_sorted (level->visible_seq, - elt, - filter_elt_cmp, NULL); - - c_path = gtk_tree_model_get_path (filter->priv->child_model, - &c_iter); - - gtk_tree_model_filter_emit_row_inserted_for_path (filter, - filter->priv->child_model, - c_path, - &c_iter); - - gtk_tree_path_free (c_path); - - /* We can immediately return, because this node was not visible - * before and its children will be checked for in response to - * the emitted row-has-child-toggled signal. - */ - return; - } - else if (elt->visible_siter) - { - if (!requested_state) - { - /* A node has turned invisible. Remove it from the level - * and emit row-deleted. Since this node is being - * deleted. it makes no sense to look further up the - * chain. - */ - gtk_tree_model_filter_remove_elt_from_level (filter, - level, elt); - return; - } - - /* Otherwise continue up the chain */ - } - else if (!elt->visible_siter) - { - if (requested_state) - { - /* A node is already in the cache, but invisible. This - * is usually a node on which a reference is kept by - * the filter model, or a node fetched on the filter's - * request, and thus not shown. Therefore, we will - * not emit row-inserted for this node. Instead, - * we signal to its parent that a change has occurred. - * - * Exception: root level, in this case, we must emit - * row-inserted. - */ - if (level->parent_level) - { - GtkTreeIter f_iter; - GtkTreePath *f_path; - - elt->visible_siter = g_sequence_insert_sorted (level->visible_seq, elt, - filter_elt_cmp, NULL); - - f_iter.stamp = filter->priv->stamp; - f_iter.user_data = level->parent_level; - f_iter.user_data2 = level->parent_elt; - - f_path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), - &f_iter); - gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (filter), - f_path, &f_iter); - gtk_tree_path_free (f_path); - } - else - { - GtkTreePath *c_path; - - elt->visible_siter = g_sequence_insert_sorted (level->visible_seq, elt, - filter_elt_cmp, NULL); - - c_path = gtk_tree_model_get_path (filter->priv->child_model, - &c_iter); - - gtk_tree_model_filter_emit_row_inserted_for_path (filter, - filter->priv->child_model, - c_path, - &c_iter); - - gtk_tree_path_free (c_path); - } - - /* We can immediately return, because this node was not visible - * before and the parent will check its children, including - * this node, in response to the emitted row-has-child-toggled - * signal. - */ - return; - } - - /* Not visible, so no need to continue. */ - return; - } - - if (!elt->children) - { - /* If an elt does not have children, these are not visible. - * Therefore, any signals emitted for these children will - * be ignored, so we do not have to emit them. - */ - return; - } - - level = elt->children; - i++; - - tmp_iter = c_iter; - gtk_tree_model_iter_nth_child (filter->priv->child_model, &c_iter, - &tmp_iter, indices[i]); - } -} - -static FilterElt * -gtk_tree_model_filter_insert_elt_in_level (GtkTreeModelFilter *filter, - GtkTreeIter *c_iter, - FilterLevel *level, - int offset, - int *index) -{ - FilterElt *elt; - GSequenceIter *siter; - - elt = filter_elt_new (); - - if (GTK_TREE_MODEL_FILTER_CACHE_CHILD_ITERS (filter)) - elt->iter = *c_iter; - - elt->offset = offset; - elt->zero_ref_count = 0; - elt->ref_count = 0; - elt->ext_ref_count = 0; - elt->children = NULL; - - /* Because we don't emit row_inserted, the node is invisible and thus - * not inserted in visible_seq - */ - elt->visible_siter = NULL; - - siter = g_sequence_insert_sorted (level->seq, elt, filter_elt_cmp, NULL); - *index = g_sequence_iter_get_position (siter); - - /* If the insert location is zero, we need to move our reference - * on the old first node to the new first node. - */ - if (*index == 0) - gtk_tree_model_filter_level_transfer_first_ref_with_index (filter, level, - 1, 0); - - return elt; -} - -static FilterElt * -gtk_tree_model_filter_fetch_child (GtkTreeModelFilter *filter, - FilterLevel *level, - int offset, - int *index) -{ - int len; - GtkTreePath *c_path = NULL; - GtkTreeIter c_iter; - GtkTreePath *c_parent_path = NULL; - GtkTreeIter c_parent_iter; - - /* check if child exists and is visible */ - if (level->parent_elt) - { - c_parent_path = - gtk_tree_model_filter_elt_get_path (level->parent_level, - level->parent_elt, - filter->priv->virtual_root); - if (!c_parent_path) - return NULL; - } - else - { - if (filter->priv->virtual_root) - c_parent_path = gtk_tree_path_copy (filter->priv->virtual_root); - else - c_parent_path = NULL; - } - - if (c_parent_path) - { - gtk_tree_model_get_iter (filter->priv->child_model, - &c_parent_iter, - c_parent_path); - len = gtk_tree_model_iter_n_children (filter->priv->child_model, - &c_parent_iter); - - c_path = gtk_tree_path_copy (c_parent_path); - gtk_tree_path_free (c_parent_path); - } - else - { - len = gtk_tree_model_iter_n_children (filter->priv->child_model, NULL); - c_path = gtk_tree_path_new (); - } - - gtk_tree_path_append_index (c_path, offset); - gtk_tree_model_get_iter (filter->priv->child_model, &c_iter, c_path); - gtk_tree_path_free (c_path); - - if (offset >= len || !gtk_tree_model_filter_visible (filter, &c_iter)) - return NULL; - - return gtk_tree_model_filter_insert_elt_in_level (filter, &c_iter, - level, offset, - index); -} - -/* Note that this function is never called from the row-deleted handler. - * This means that this function is only used for removing elements - * which are still present in the child model. As a result, we must - * take care to properly release the references the filter model has - * on the child model nodes. - */ -static void -gtk_tree_model_filter_remove_elt_from_level (GtkTreeModelFilter *filter, - FilterLevel *level, - FilterElt *elt) -{ - FilterElt *parent; - FilterLevel *parent_level; - int length, orig_level_ext_ref_count; - GtkTreeIter iter; - GtkTreePath *path = NULL; - - gboolean emit_child_toggled = FALSE; - - /* We need to know about the level's ext ref count before removal - * of this node. - */ - orig_level_ext_ref_count = level->ext_ref_count; - - iter.stamp = filter->priv->stamp; - iter.user_data = level; - iter.user_data2 = elt; - - parent = level->parent_elt; - parent_level = level->parent_level; - - if (!parent || orig_level_ext_ref_count > 0) - path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter); - else - /* If the level is not visible, the parent is potentially invisible - * too. Either way, as no signal will be emitted, there is no use - * for a path. - */ - path = NULL; - - length = g_sequence_get_length (level->seq); - - /* first register the node to be invisible */ - g_sequence_remove (elt->visible_siter); - elt->visible_siter = NULL; - - /* - * If level != root level and the number of visible nodes is 0 (ie. this - * is the last node to be removed from the level), emit - * row-has-child-toggled. - */ - - if (level != filter->priv->root - && g_sequence_get_length (level->visible_seq) == 0 - && parent - && parent->visible_siter) - emit_child_toggled = TRUE; - - /* Distinguish: - * - length > 1: in this case, the node is removed from the level - * and row-deleted is emitted. - * - length == 1: in this case, we need to decide whether to keep - * the level or to free it. - */ - if (length > 1) - { - GSequenceIter *siter; - - /* We emit row-deleted, and remove the node from the cache. - * If it has any children, these will be removed here as well. - */ - - /* FIXME: I am not 100% sure it is always save to fully free the - * level here. Perhaps the state of the parent level, etc. has to - * be checked to make the right decision, like is done below for - * the case length == 1. - */ - if (elt->children) - gtk_tree_model_filter_free_level (filter, elt->children, TRUE, TRUE, TRUE); - - /* If the first node is being removed, transfer, the reference */ - if (elt == g_sequence_get (g_sequence_get_begin_iter (level->seq))) - { - gtk_tree_model_filter_level_transfer_first_ref_with_index (filter, level, - 0, 1); - } - - while (elt->ext_ref_count > 0) - gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter), - &iter, TRUE, TRUE); - /* In this case, we do remove reference counts we've added ourselves, - * since the node will be removed from the data structures. - */ - while (elt->ref_count > 0) - gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter), - &iter, FALSE, TRUE); - - /* remove the node */ - lookup_elt_with_offset (level->seq, elt->offset, &siter); - g_sequence_remove (siter); - - gtk_tree_model_filter_increment_stamp (filter); - - /* Only if the node is in the root level (parent == NULL) or - * the level is visible, a row-deleted signal is necessary. - */ - if (!parent || orig_level_ext_ref_count > 0) - gtk_tree_model_row_deleted (GTK_TREE_MODEL (filter), path); - } - else - { - /* There is only one node left in this level */ -#ifdef MODEL_FILTER_DEBUG - g_assert (length == 1); -#endif - - /* The row is signalled as deleted to the client. We have to - * drop the remaining external reference count here, the client - * will not do it. - * - * We keep the reference counts we've obtained ourselves. - */ - while (elt->ext_ref_count > 0) - gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter), - &iter, TRUE, TRUE); - - /* This level is still required if: - * - it is the root level - * - its parent level is the root level - * - its parent level has an external ref count > 0 - */ - if (! (level == filter->priv->root || - level->parent_level == filter->priv->root || - level->parent_level->ext_ref_count > 0)) - { - /* Otherwise, the level can be removed */ - gtk_tree_model_filter_free_level (filter, level, TRUE, TRUE, TRUE); - } - else - { - /* Level is kept, but we turn our attention to a child level. - * - * If level is not the root level, it is a child level with - * an ext ref count that is now 0. That means that any child level - * of elt can be removed. - */ - if (level != filter->priv->root) - { -#ifdef MODEL_FILTER_DEBUG - g_assert (level->ext_ref_count == 0); -#endif - if (elt->children) - gtk_tree_model_filter_free_level (filter, elt->children, - TRUE, TRUE, TRUE); - } - else - { - /* In this case, we want to keep the level with the first - * node pulled in to monitor for signals. - */ - if (elt->children) - gtk_tree_model_filter_prune_level (filter, elt->children); - } - } - - if (!parent || orig_level_ext_ref_count > 0) - gtk_tree_model_row_deleted (GTK_TREE_MODEL (filter), path); - } - - gtk_tree_path_free (path); - - if (emit_child_toggled && parent->ext_ref_count > 0) - { - GtkTreeIter piter; - GtkTreePath *ppath; - - piter.stamp = filter->priv->stamp; - piter.user_data = parent_level; - piter.user_data2 = parent; - - ppath = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &piter); - - gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (filter), - ppath, &piter); - gtk_tree_path_free (ppath); - } -} - -/* This function is called after the given node has become visible. - * When the node has children, we should build the level and - * take a reference on the first child. - */ -static void -gtk_tree_model_filter_update_children (GtkTreeModelFilter *filter, - FilterLevel *level, - FilterElt *elt) -{ - GtkTreeIter c_iter; - GtkTreeIter iter; - - if (!elt->visible_siter) - return; - - iter.stamp = filter->priv->stamp; - iter.user_data = level; - iter.user_data2 = elt; - - gtk_tree_model_filter_convert_iter_to_child_iter (filter, &c_iter, &iter); - - if ((!level->parent_level || level->parent_level->ext_ref_count > 0) && - gtk_tree_model_iter_has_child (filter->priv->child_model, &c_iter)) - { - if (!elt->children) - gtk_tree_model_filter_build_level (filter, level, elt, FALSE); - - if (elt->ext_ref_count > 0 && elt->children && - g_sequence_get_length (elt->children->seq)) - { - GtkTreePath *path; - path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter); - gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (filter), - path, - &iter); - if (path) - gtk_tree_path_free (path); - } - } -} - -/* Path is relative to the child model (this is on search on elt offset) - * but with the virtual root already removed if necesssary. - */ -static gboolean -find_elt_with_offset (GtkTreeModelFilter *filter, - GtkTreePath *path, - FilterLevel **level_, - FilterElt **elt_) -{ - int i = 0; - FilterLevel *level; - FilterLevel *parent_level = NULL; - FilterElt *elt = NULL; - - level = FILTER_LEVEL (filter->priv->root); - - while (i < gtk_tree_path_get_depth (path)) - { - if (!level) - return FALSE; - - elt = lookup_elt_with_offset (level->seq, - gtk_tree_path_get_indices (path)[i], - NULL); - - if (!elt) - return FALSE; - - parent_level = level; - level = elt->children; - i++; - } - - if (level_) - *level_ = parent_level; - - if (elt_) - *elt_ = elt; - - return TRUE; -} - -/* TreeModel signals */ -static void -gtk_tree_model_filter_emit_row_inserted_for_path (GtkTreeModelFilter *filter, - GtkTreeModel *c_model, - GtkTreePath *c_path, - GtkTreeIter *c_iter) -{ - FilterLevel *level; - FilterElt *elt; - GtkTreePath *path; - GtkTreeIter iter, children; - gboolean signals_emitted = FALSE; - - if (!filter->priv->root) - { - /* The root level has not been exposed to the view yet, so we - * need to emit signals for any node that is being inserted. - */ - gtk_tree_model_filter_build_level (filter, NULL, NULL, TRUE); - - /* Check if the root level was built. Then child levels - * that matter have also been built (due to update_children, - * which triggers iter_n_children). - */ - if (filter->priv->root && - g_sequence_get_length (FILTER_LEVEL (filter->priv->root)->visible_seq) > 0) - signals_emitted = TRUE; - } - - gtk_tree_model_filter_increment_stamp (filter); - - /* We need to disallow to build new levels, because we are then pulling - * in a child in an invisible level. We only want to find path if it - * is in a visible level (and thus has a parent that is visible). - */ - path = gtk_real_tree_model_filter_convert_child_path_to_path (filter, - c_path, - FALSE, - TRUE); - - if (!path) - /* parent is probably being filtered out */ - return; - - gtk_tree_model_filter_get_iter_full (GTK_TREE_MODEL (filter), &iter, path); - - level = FILTER_LEVEL (iter.user_data); - elt = FILTER_ELT (iter.user_data2); - - /* Make sure elt is visible. elt can already be visible in case - * it was pulled in above, so avoid inserted it into visible_seq twice. - */ - if (!elt->visible_siter) - { - elt->visible_siter = g_sequence_insert_sorted (level->visible_seq, - elt, filter_elt_cmp, - NULL); - } - - /* Check whether the node and all of its parents are visible */ - if (gtk_tree_model_filter_elt_is_visible_in_target (level, elt)) - { - /* visibility changed -- reget path */ - gtk_tree_path_free (path); - path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter); - - if (!signals_emitted && - (!level->parent_level || level->ext_ref_count > 0)) - gtk_tree_model_row_inserted (GTK_TREE_MODEL (filter), path, &iter); - - if (level->parent_level && level->parent_elt->ext_ref_count > 0 && - g_sequence_get_length (level->visible_seq) == 1) - { - /* We know that this is the first visible node in this level, so - * we need to emit row-has-child-toggled on the parent. This - * does not apply to the root level. - */ - - gtk_tree_path_up (path); - gtk_tree_model_get_iter (GTK_TREE_MODEL (filter), &iter, path); - - gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (filter), - path, - &iter); - } - - if (!signals_emitted - && gtk_tree_model_iter_children (c_model, &children, c_iter)) - gtk_tree_model_filter_update_children (filter, level, elt); - } - - gtk_tree_path_free (path); -} - -static void -gtk_tree_model_filter_row_changed (GtkTreeModel *c_model, - GtkTreePath *c_path, - GtkTreeIter *c_iter, - gpointer data) -{ - GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (data); - GtkTreeIter iter; - GtkTreeIter children; - GtkTreeIter real_c_iter; - GtkTreePath *path = NULL; - GtkTreePath *real_path = NULL; - - FilterElt *elt; - FilterLevel *level; - - gboolean requested_state; - gboolean current_state; - gboolean free_c_path = FALSE; - - g_return_if_fail (c_path != NULL || c_iter != NULL); - - if (!c_path) - { - c_path = gtk_tree_model_get_path (c_model, c_iter); - free_c_path = TRUE; - } - - if (filter->priv->virtual_root) - real_path = gtk_tree_model_filter_remove_root (c_path, - filter->priv->virtual_root); - else - real_path = gtk_tree_path_copy (c_path); - - if (c_iter) - real_c_iter = *c_iter; - else - gtk_tree_model_get_iter (c_model, &real_c_iter, c_path); - - /* is this node above the virtual root? */ - if (filter->priv->virtual_root && - (gtk_tree_path_get_depth (filter->priv->virtual_root) - >= gtk_tree_path_get_depth (c_path))) - goto done; - - /* what's the requested state? */ - requested_state = gtk_tree_model_filter_visible (filter, &real_c_iter); - - /* now, let's see whether the item is there */ - path = gtk_real_tree_model_filter_convert_child_path_to_path (filter, - c_path, - FALSE, - FALSE); - - if (path) - { - gtk_tree_model_filter_get_iter_full (GTK_TREE_MODEL (filter), - &iter, path); - current_state = FILTER_ELT (iter.user_data2)->visible_siter != NULL; - } - else - current_state = FALSE; - - if (current_state == FALSE && requested_state == FALSE) - /* no changes required */ - goto done; - - if (current_state == TRUE && requested_state == FALSE) - { - gtk_tree_model_filter_remove_elt_from_level (filter, - FILTER_LEVEL (iter.user_data), - FILTER_ELT (iter.user_data2)); - - if (real_path) - gtk_tree_model_filter_check_ancestors (filter, real_path); - - goto done; - } - - if (current_state == TRUE && requested_state == TRUE) - { - level = FILTER_LEVEL (iter.user_data); - elt = FILTER_ELT (iter.user_data2); - - if (gtk_tree_model_filter_elt_is_visible_in_target (level, elt)) - { - /* propagate the signal; also get a path taking only visible - * nodes into account. - */ - gtk_tree_path_free (path); - path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter); - - if (level->ext_ref_count > 0) - gtk_tree_model_row_changed (GTK_TREE_MODEL (filter), path, &iter); - - /* and update the children */ - if (gtk_tree_model_iter_children (c_model, &children, &real_c_iter)) - gtk_tree_model_filter_update_children (filter, level, elt); - } - - if (real_path) - gtk_tree_model_filter_check_ancestors (filter, real_path); - - goto done; - } - - /* only current == FALSE and requested == TRUE is left, - * pull in the child - */ - g_return_if_fail (current_state == FALSE && requested_state == TRUE); - - if (real_path) - gtk_tree_model_filter_check_ancestors (filter, real_path); - - gtk_tree_model_filter_emit_row_inserted_for_path (filter, c_model, - c_path, c_iter); - -done: - if (path) - gtk_tree_path_free (path); - - if (real_path) - gtk_tree_path_free (real_path); - - if (free_c_path) - gtk_tree_path_free (c_path); -} - -static void -gtk_tree_model_filter_row_inserted (GtkTreeModel *c_model, - GtkTreePath *c_path, - GtkTreeIter *c_iter, - gpointer data) -{ - GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (data); - GtkTreePath *real_path = NULL; - - GtkTreeIter real_c_iter; - - FilterElt *elt = NULL; - FilterLevel *level = NULL; - FilterLevel *parent_level = NULL; - GSequenceIter *siter; - FilterElt dummy; - - int i = 0, offset; - - gboolean free_c_path = FALSE; - gboolean emit_row_inserted = FALSE; - - g_return_if_fail (c_path != NULL || c_iter != NULL); - - if (!c_path) - { - c_path = gtk_tree_model_get_path (c_model, c_iter); - free_c_path = TRUE; - } - - if (c_iter) - real_c_iter = *c_iter; - else - gtk_tree_model_get_iter (c_model, &real_c_iter, c_path); - - /* the row has already been inserted. so we need to fixup the - * virtual root here first - */ - if (filter->priv->virtual_root) - { - if (gtk_tree_path_get_depth (filter->priv->virtual_root) >= - gtk_tree_path_get_depth (c_path)) - { - int depth; - int *v_indices, *c_indices; - gboolean common_prefix = TRUE; - - depth = gtk_tree_path_get_depth (c_path) - 1; - v_indices = gtk_tree_path_get_indices (filter->priv->virtual_root); - c_indices = gtk_tree_path_get_indices (c_path); - - for (i = 0; i < depth; i++) - if (v_indices[i] != c_indices[i]) - { - common_prefix = FALSE; - break; - } - - if (common_prefix && v_indices[depth] >= c_indices[depth]) - (v_indices[depth])++; - } - } - - /* subtract virtual root if necessary */ - if (filter->priv->virtual_root) - { - real_path = gtk_tree_model_filter_remove_root (c_path, - filter->priv->virtual_root); - /* not our child */ - if (!real_path) - goto done; - } - else - real_path = gtk_tree_path_copy (c_path); - - if (!filter->priv->root) - { - /* The root level has not been exposed to the view yet, so we - * need to emit signals for any node that is being inserted. - */ - gtk_tree_model_filter_build_level (filter, NULL, NULL, TRUE); - - /* Check if the root level was built. Then child levels - * that matter have also been built (due to update_children, - * which triggers iter_n_children). - */ - if (filter->priv->root) - { - emit_row_inserted = FALSE; - goto done; - } - } - - if (gtk_tree_path_get_depth (real_path) - 1 >= 1) - { - gboolean found = FALSE; - GtkTreePath *parent = gtk_tree_path_copy (real_path); - gtk_tree_path_up (parent); - - found = find_elt_with_offset (filter, parent, &parent_level, &elt); - - gtk_tree_path_free (parent); - - if (!found) - /* Parent is not in the cache and probably being filtered out */ - goto done; - - level = elt->children; - } - else - level = FILTER_LEVEL (filter->priv->root); - - if (!level) - { - if (elt && elt->visible_siter) - { - /* The level in which the new node should be inserted does not - * exist, but the parent, elt, does. If elt is visible, emit - * row-has-child-toggled. - */ - GtkTreePath *tmppath; - GtkTreeIter tmpiter; - - tmpiter.stamp = filter->priv->stamp; - tmpiter.user_data = parent_level; - tmpiter.user_data2 = elt; - - tmppath = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), - &tmpiter); - - if (tmppath) - { - gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (filter), - tmppath, &tmpiter); - gtk_tree_path_free (tmppath); - } - } - goto done; - } - - /* let's try to insert the value */ - offset = gtk_tree_path_get_indices (real_path)[gtk_tree_path_get_depth (real_path) - 1]; - - /* update the offsets, yes if we didn't insert the node above, there will - * be a gap here. This will be filled with the node (via fetch_child) when - * it becomes visible - */ - dummy.offset = offset; - siter = g_sequence_search (level->seq, &dummy, filter_elt_cmp, NULL); - siter = g_sequence_iter_prev (siter); - g_sequence_foreach_range (siter, g_sequence_get_end_iter (level->seq), - increase_offset_iter, GINT_TO_POINTER (offset)); - - /* only insert when visible */ - if (gtk_tree_model_filter_visible (filter, &real_c_iter)) - { - FilterElt *felt; - - felt = gtk_tree_model_filter_insert_elt_in_level (filter, - &real_c_iter, - level, offset, - &i); - - /* insert_elt_in_level defaults to FALSE */ - felt->visible_siter = g_sequence_insert_sorted (level->visible_seq, - felt, - filter_elt_cmp, NULL); - emit_row_inserted = TRUE; - } - -done: - if (real_path) - gtk_tree_model_filter_check_ancestors (filter, real_path); - - if (emit_row_inserted) - gtk_tree_model_filter_emit_row_inserted_for_path (filter, c_model, - c_path, c_iter); - - if (real_path) - gtk_tree_path_free (real_path); - - if (free_c_path) - gtk_tree_path_free (c_path); -} - -static void -gtk_tree_model_filter_row_has_child_toggled (GtkTreeModel *c_model, - GtkTreePath *c_path, - GtkTreeIter *c_iter, - gpointer data) -{ - GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (data); - GtkTreePath *path; - GtkTreeIter iter; - FilterLevel *level; - FilterElt *elt; - gboolean requested_state; - - g_return_if_fail (c_path != NULL && c_iter != NULL); - - /* If we get row-has-child-toggled on the virtual root, and there is - * no root level; try to build it now. - */ - if (filter->priv->virtual_root && !filter->priv->root - && !gtk_tree_path_compare (c_path, filter->priv->virtual_root)) - { - gtk_tree_model_filter_build_level (filter, NULL, NULL, TRUE); - return; - } - - /* For all other levels, there is a chance that the visibility state - * of the parent has changed now. - */ - - path = gtk_real_tree_model_filter_convert_child_path_to_path (filter, - c_path, - FALSE, - TRUE); - if (!path) - return; - - gtk_tree_model_filter_get_iter_full (GTK_TREE_MODEL (data), &iter, path); - - level = FILTER_LEVEL (iter.user_data); - elt = FILTER_ELT (iter.user_data2); - - gtk_tree_path_free (path); - - requested_state = gtk_tree_model_filter_visible (filter, c_iter); - - if (!elt->visible_siter && !requested_state) - { - /* The parent node currently is not visible and will not become - * visible, so we will not pass on the row-has-child-toggled event. - */ - return; - } - else if (elt->visible_siter && !requested_state) - { - /* The node is no longer visible, so it has to be removed. - * _remove_elt_from_level() takes care of emitting row-has-child-toggled - * when required. - */ - gtk_tree_model_filter_remove_elt_from_level (filter, level, elt); - - return; - } - else if (!elt->visible_siter && requested_state) - { - elt->visible_siter = g_sequence_insert_sorted (level->visible_seq, - elt, filter_elt_cmp, - NULL); - - /* Only insert if the parent is visible in the target */ - if (gtk_tree_model_filter_elt_is_visible_in_target (level, elt)) - { - path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter); - gtk_tree_model_row_inserted (GTK_TREE_MODEL (filter), path, &iter); - gtk_tree_path_free (path); - - /* We do not update children now, because that will happen - * below. - */ - } - } - /* For the remaining possibility, elt->visible && requested_state - * no action is required. - */ - - /* If this node is referenced and has children, build the level so we - * can monitor it for changes. - */ - if (elt->ref_count > 1 && !elt->children && - gtk_tree_model_iter_has_child (c_model, c_iter)) - gtk_tree_model_filter_build_level (filter, level, elt, FALSE); - - /* get a path taking only visible nodes into account */ - path = gtk_tree_model_get_path (GTK_TREE_MODEL (data), &iter); - gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (data), path, &iter); - gtk_tree_path_free (path); -} - -static void -gtk_tree_model_filter_virtual_root_deleted (GtkTreeModelFilter *filter, - GtkTreePath *c_path) -{ - int i, nodes; - GtkTreePath *path; - FilterLevel *level = FILTER_LEVEL (filter->priv->root); - - /* The virtual root (or one of its ancestors) has been deleted. This - * means that all content for our model is now gone. We deal with - * this by removing everything in the filter model: we just iterate - * over the root level and emit a row-deleted for each FilterElt. - * (FIXME: Should we emit row-deleted for child nodes as well? This - * has never been fully clear in TreeModel). - */ - - /* We unref the path of the virtual root, up to and not including the - * deleted node which can no longer be unreffed. - */ - gtk_tree_model_filter_unref_path (filter, filter->priv->virtual_root, - gtk_tree_path_get_depth (c_path) - 1); - filter->priv->virtual_root_deleted = TRUE; - - if (!level) - return; - - nodes = g_sequence_get_length (level->visible_seq); - - /* We should not propagate the unref here. An unref for any of these - * nodes will fail, since the respective nodes in the child model are - * no longer there. - */ - gtk_tree_model_filter_free_level (filter, filter->priv->root, FALSE, TRUE, FALSE); - - gtk_tree_model_filter_increment_stamp (filter); - - path = gtk_tree_path_new (); - gtk_tree_path_append_index (path, 0); - - for (i = 0; i < nodes; i++) - gtk_tree_model_row_deleted (GTK_TREE_MODEL (filter), path); - - gtk_tree_path_free (path); -} - -static void -gtk_tree_model_filter_adjust_virtual_root (GtkTreeModelFilter *filter, - GtkTreePath *c_path) -{ - int i; - int level; - int *v_indices, *c_indices; - gboolean common_prefix = TRUE; - - level = gtk_tree_path_get_depth (c_path) - 1; - v_indices = gtk_tree_path_get_indices (filter->priv->virtual_root); - c_indices = gtk_tree_path_get_indices (c_path); - - for (i = 0; i < level; i++) - if (v_indices[i] != c_indices[i]) - { - common_prefix = FALSE; - break; - } - - if (common_prefix && v_indices[level] > c_indices[level]) - (v_indices[level])--; -} - -static void -gtk_tree_model_filter_row_deleted_invisible_node (GtkTreeModelFilter *filter, - GtkTreePath *c_path) -{ - int offset; - GtkTreePath *real_path; - FilterLevel *level; - FilterElt *elt; - FilterElt dummy; - GSequenceIter *siter; - - /* The node deleted in the child model is not visible in the - * filter model. We will not emit a signal, just fixup the offsets - * of the other nodes. - */ - - if (!filter->priv->root) - return; - - level = FILTER_LEVEL (filter->priv->root); - - /* subtract vroot if necessary */ - if (filter->priv->virtual_root) - { - real_path = gtk_tree_model_filter_remove_root (c_path, - filter->priv->virtual_root); - /* we don't handle this */ - if (!real_path) - return; - } - else - real_path = gtk_tree_path_copy (c_path); - - if (gtk_tree_path_get_depth (real_path) - 1 >= 1) - { - gboolean found = FALSE; - GtkTreePath *parent = gtk_tree_path_copy (real_path); - gtk_tree_path_up (parent); - - found = find_elt_with_offset (filter, parent, &level, &elt); - - gtk_tree_path_free (parent); - - if (!found) - { - /* parent is filtered out, so no level */ - gtk_tree_path_free (real_path); - return; - } - - level = elt->children; - } - - offset = gtk_tree_path_get_indices (real_path)[gtk_tree_path_get_depth (real_path) - 1]; - gtk_tree_path_free (real_path); - - if (!level) - return; - - /* decrease offset of all nodes following the deleted node */ - dummy.offset = offset; - siter = g_sequence_search (level->seq, &dummy, filter_elt_cmp, NULL); - g_sequence_foreach_range (siter, g_sequence_get_end_iter (level->seq), - decrease_offset_iter, GINT_TO_POINTER (offset)); -} - -static void -gtk_tree_model_filter_row_deleted (GtkTreeModel *c_model, - GtkTreePath *c_path, - gpointer data) -{ - GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (data); - GtkTreePath *path; - GtkTreeIter iter; - FilterElt *elt, *parent_elt = NULL; - FilterLevel *level, *parent_level = NULL; - GSequenceIter *siter; - gboolean emit_child_toggled = FALSE; - gboolean emit_row_deleted = FALSE; - int offset; - int orig_level_ext_ref_count; - - g_return_if_fail (c_path != NULL); - - /* special case the deletion of an ancestor of the virtual root */ - if (filter->priv->virtual_root && - (gtk_tree_path_is_ancestor (c_path, filter->priv->virtual_root) || - !gtk_tree_path_compare (c_path, filter->priv->virtual_root))) - { - gtk_tree_model_filter_virtual_root_deleted (filter, c_path); - return; - } - - /* adjust the virtual root for the deleted row */ - if (filter->priv->virtual_root && - gtk_tree_path_get_depth (filter->priv->virtual_root) >= - gtk_tree_path_get_depth (c_path)) - gtk_tree_model_filter_adjust_virtual_root (filter, c_path); - - path = gtk_real_tree_model_filter_convert_child_path_to_path (filter, - c_path, - FALSE, - FALSE); - - if (!path) - { - gtk_tree_model_filter_row_deleted_invisible_node (filter, c_path); - return; - } - - /* a node was deleted, which was in our cache */ - gtk_tree_model_filter_get_iter_full (GTK_TREE_MODEL (data), &iter, path); - - level = FILTER_LEVEL (iter.user_data); - elt = FILTER_ELT (iter.user_data2); - offset = elt->offset; - orig_level_ext_ref_count = level->ext_ref_count; - - if (elt->visible_siter) - { - /* get a path taking only visible nodes into account */ - gtk_tree_path_free (path); - path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter); - - if (g_sequence_get_length (level->visible_seq) == 1) - { - emit_child_toggled = TRUE; - parent_level = level->parent_level; - parent_elt = level->parent_elt; - } - - emit_row_deleted = TRUE; - } - - /* Release the references on this node, without propagation because - * the node does not exist anymore in the child model. The filter - * model's references on the node in case of level->parent or use - * of a virtual root are automatically destroyed by the child model. - */ - while (elt->ext_ref_count > 0) - gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (data), &iter, - TRUE, FALSE); - - if (elt->children) - /* If this last node has children, then the recursion in free_level - * will release this reference. - */ - while (elt->ref_count > 1) - gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (data), &iter, - FALSE, FALSE); - else - while (elt->ref_count > 0) - gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (data), &iter, - FALSE, FALSE); - - - if (g_sequence_get_length (level->seq) == 1) - { - /* kill level */ - gtk_tree_model_filter_free_level (filter, level, FALSE, TRUE, FALSE); - } - else - { - GSequenceIter *tmp; - gboolean is_first; - - lookup_elt_with_offset (level->seq, elt->offset, &siter); - is_first = g_sequence_get_begin_iter (level->seq) == siter; - - if (elt->children) - gtk_tree_model_filter_free_level (filter, elt->children, - FALSE, FALSE, FALSE); - - /* remove the row */ - if (elt->visible_siter) - g_sequence_remove (elt->visible_siter); - tmp = g_sequence_iter_next (siter); - g_sequence_remove (siter); - g_sequence_foreach_range (tmp, g_sequence_get_end_iter (level->seq), - decrease_offset_iter, GINT_TO_POINTER (offset)); - - /* Take a reference on the new first node. The first node previously - * keeping this reference has been removed above. - */ - if (is_first) - { - GtkTreeIter f_iter; - - f_iter.stamp = filter->priv->stamp; - f_iter.user_data = level; - f_iter.user_data2 = g_sequence_get (g_sequence_get_begin_iter (level->seq)); - - gtk_tree_model_filter_real_ref_node (GTK_TREE_MODEL (filter), - &f_iter, FALSE); - } - } - - if (emit_row_deleted) - { - /* emit row_deleted */ - gtk_tree_model_filter_increment_stamp (filter); - - if (!parent_elt || orig_level_ext_ref_count > 0) - gtk_tree_model_row_deleted (GTK_TREE_MODEL (data), path); - } - - if (emit_child_toggled && parent_level) - { - GtkTreeIter iter2; - GtkTreePath *path2; - - iter2.stamp = filter->priv->stamp; - iter2.user_data = parent_level; - iter2.user_data2 = parent_elt; - - /* We set in_row_deleted to TRUE to avoid a level build triggered - * by row-has-child-toggled (parent model could call iter_has_child - * for example). - */ - filter->priv->in_row_deleted = TRUE; - path2 = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter2); - gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (filter), - path2, &iter2); - gtk_tree_path_free (path2); - filter->priv->in_row_deleted = FALSE; - } - - if (filter->priv->virtual_root) - { - GtkTreePath *real_path; - - real_path = gtk_tree_model_filter_remove_root (c_path, - filter->priv->virtual_root); - if (real_path) - { - gtk_tree_model_filter_check_ancestors (filter, real_path); - gtk_tree_path_free (real_path); - } - } - else - gtk_tree_model_filter_check_ancestors (filter, c_path); - - gtk_tree_path_free (path); -} - -static void -gtk_tree_model_filter_rows_reordered (GtkTreeModel *c_model, - GtkTreePath *c_path, - GtkTreeIter *c_iter, - int *new_order, - gpointer data) -{ - FilterElt *elt; - FilterLevel *level; - GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (data); - - GtkTreePath *path; - GtkTreeIter iter; - - GSequence *tmp_seq; - GSequenceIter *tmp_end_iter; - GSequenceIter *old_first_siter = NULL; - int *tmp_array; - int i, elt_count; - int length; - - g_return_if_fail (new_order != NULL); - - if (c_path == NULL || gtk_tree_path_get_depth (c_path) == 0) - { - length = gtk_tree_model_iter_n_children (c_model, NULL); - - if (filter->priv->virtual_root) - { - int new_pos = -1; - - /* reorder root level of path */ - for (i = 0; i < length; i++) - if (new_order[i] == gtk_tree_path_get_indices (filter->priv->virtual_root)[0]) - new_pos = i; - - if (new_pos < 0) - return; - - gtk_tree_path_get_indices (filter->priv->virtual_root)[0] = new_pos; - return; - } - - path = gtk_tree_path_new (); - level = FILTER_LEVEL (filter->priv->root); - } - else - { - GtkTreeIter child_iter; - - /* virtual root anchor reordering */ - if (filter->priv->virtual_root && - gtk_tree_path_is_ancestor (c_path, filter->priv->virtual_root)) - { - int new_pos = -1; - int len; - int depth; - GtkTreeIter real_c_iter; - - depth = gtk_tree_path_get_depth (c_path); - - if (c_iter) - real_c_iter = *c_iter; - else - gtk_tree_model_get_iter (c_model, &real_c_iter, c_path); - - len = gtk_tree_model_iter_n_children (c_model, &real_c_iter); - - for (i = 0; i < len; i++) - if (new_order[i] == gtk_tree_path_get_indices (filter->priv->virtual_root)[depth]) - new_pos = i; - - if (new_pos < 0) - return; - - gtk_tree_path_get_indices (filter->priv->virtual_root)[depth] = new_pos; - return; - } - - path = gtk_real_tree_model_filter_convert_child_path_to_path (filter, - c_path, - FALSE, - FALSE); - - if (!path && filter->priv->virtual_root && - gtk_tree_path_compare (c_path, filter->priv->virtual_root)) - return; - - if (!path && !filter->priv->virtual_root) - return; - - if (!path) - { - /* root level mode */ - if (!c_iter) - gtk_tree_model_get_iter (c_model, c_iter, c_path); - length = gtk_tree_model_iter_n_children (c_model, c_iter); - path = gtk_tree_path_new (); - level = FILTER_LEVEL (filter->priv->root); - } - else - { - gtk_tree_model_filter_get_iter_full (GTK_TREE_MODEL (data), - &iter, path); - - elt = FILTER_ELT (iter.user_data2); - - if (!elt->children) - { - gtk_tree_path_free (path); - return; - } - - level = elt->children; - - gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (filter), &child_iter, &iter); - length = gtk_tree_model_iter_n_children (c_model, &child_iter); - } - } - - if (!level || g_sequence_get_length (level->seq) < 1) - { - gtk_tree_path_free (path); - return; - } - - /* NOTE: we do not bail out here if level->seq->len < 2 like - * GtkTreeModelSort does. This because we do some special tricky - * reordering. - */ - - tmp_seq = g_sequence_new (filter_elt_free); - tmp_end_iter = g_sequence_get_end_iter (tmp_seq); - tmp_array = g_new (int, g_sequence_get_length (level->visible_seq)); - elt_count = 0; - - old_first_siter = g_sequence_get_iter_at_pos (level->seq, 0); - - for (i = 0; i < length; i++) - { - GSequenceIter *siter; - - elt = lookup_elt_with_offset (level->seq, new_order[i], &siter); - if (elt == NULL) - continue; - - /* Only for visible items an entry should be present in the order array - * to be emitted. - */ - if (elt->visible_siter) - tmp_array[elt_count++] = g_sequence_iter_get_position (elt->visible_siter); - - /* Steal elt from level->seq and append it to tmp_seq */ - g_sequence_move (siter, tmp_end_iter); - elt->offset = i; - } - - g_warn_if_fail (g_sequence_get_length (level->seq) == 0); - g_sequence_free (level->seq); - level->seq = tmp_seq; - g_sequence_sort (level->visible_seq, filter_elt_cmp, NULL); - - /* Transfer the reference from the old item at position 0 to the - * new item at position 0, unless the old item at position 0 is also - * at position 0 in the new sequence. - */ - if (g_sequence_iter_get_position (old_first_siter) != 0) - gtk_tree_model_filter_level_transfer_first_ref (filter, - level, - old_first_siter, - g_sequence_get_iter_at_pos (level->seq, 0)); - - /* emit rows_reordered */ - if (g_sequence_get_length (level->visible_seq) > 0) - { - if (!gtk_tree_path_get_indices (path)) - gtk_tree_model_rows_reordered (GTK_TREE_MODEL (data), path, NULL, - tmp_array); - else - { - /* get a path taking only visible nodes into account */ - gtk_tree_path_free (path); - path = gtk_tree_model_get_path (GTK_TREE_MODEL (data), &iter); - - gtk_tree_model_rows_reordered (GTK_TREE_MODEL (data), path, &iter, - tmp_array); - } - } - - /* done */ - g_free (tmp_array); - gtk_tree_path_free (path); -} - -/* TreeModelIface implementation */ -static GtkTreeModelFlags -gtk_tree_model_filter_get_flags (GtkTreeModel *model) -{ - GtkTreeModelFlags flags; - - g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), 0); - g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->child_model != NULL, 0); - - flags = gtk_tree_model_get_flags (GTK_TREE_MODEL_FILTER (model)->priv->child_model); - - if ((flags & GTK_TREE_MODEL_LIST_ONLY) == GTK_TREE_MODEL_LIST_ONLY) - return GTK_TREE_MODEL_LIST_ONLY; - - return 0; -} - -static int -gtk_tree_model_filter_get_n_columns (GtkTreeModel *model) -{ - GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model; - - g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), 0); - g_return_val_if_fail (filter->priv->child_model != NULL, 0); - - if (filter->priv->child_model == NULL) - return 0; - - /* so we can't set the modify func after this ... */ - filter->priv->modify_func_set = TRUE; - - if (filter->priv->modify_n_columns > 0) - return filter->priv->modify_n_columns; - - return gtk_tree_model_get_n_columns (filter->priv->child_model); -} - -static GType -gtk_tree_model_filter_get_column_type (GtkTreeModel *model, - int index) -{ - GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model; - - g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), G_TYPE_INVALID); - g_return_val_if_fail (filter->priv->child_model != NULL, G_TYPE_INVALID); - - /* so we can't set the modify func after this ... */ - filter->priv->modify_func_set = TRUE; - - if (filter->priv->modify_types) - { - g_return_val_if_fail (index < filter->priv->modify_n_columns, G_TYPE_INVALID); - - return filter->priv->modify_types[index]; - } - - return gtk_tree_model_get_column_type (filter->priv->child_model, index); -} - -/* A special case of _get_iter; this function can also get iters which - * are not visible. These iters should ONLY be passed internally, never - * pass those along with a signal emission. - */ -static gboolean -gtk_tree_model_filter_get_iter_full (GtkTreeModel *model, - GtkTreeIter *iter, - GtkTreePath *path) -{ - GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model; - int *indices; - FilterLevel *level; - FilterElt *elt; - int depth, i; - GSequenceIter *siter; - - g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), FALSE); - g_return_val_if_fail (filter->priv->child_model != NULL, FALSE); - - indices = gtk_tree_path_get_indices (path); - - if (filter->priv->root == NULL) - gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE); - level = FILTER_LEVEL (filter->priv->root); - - depth = gtk_tree_path_get_depth (path); - if (!depth) - { - iter->stamp = 0; - return FALSE; - } - - for (i = 0; i < depth - 1; i++) - { - if (!level || indices[i] >= g_sequence_get_length (level->seq)) - { - iter->stamp = 0; - return FALSE; - } - - siter = g_sequence_get_iter_at_pos (level->seq, indices[i]); - if (g_sequence_iter_is_end (siter)) - { - iter->stamp = 0; - return FALSE; - } - - elt = GET_ELT (siter); - g_assert (elt); - - if (!elt->children) - gtk_tree_model_filter_build_level (filter, level, elt, FALSE); - level = elt->children; - } - - if (!level || indices[i] >= g_sequence_get_length (level->seq)) - { - iter->stamp = 0; - return FALSE; - } - - iter->stamp = filter->priv->stamp; - iter->user_data = level; - - siter = g_sequence_get_iter_at_pos (level->seq, indices[depth - 1]); - if (g_sequence_iter_is_end (siter)) - { - iter->stamp = 0; - return FALSE; - } - iter->user_data2 = GET_ELT (siter); - - return TRUE; -} - -static gboolean -gtk_tree_model_filter_get_iter (GtkTreeModel *model, - GtkTreeIter *iter, - GtkTreePath *path) -{ - GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model; - int *indices; - FilterLevel *level; - FilterElt *elt; - GSequenceIter *siter; - int depth, i; - - g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), FALSE); - g_return_val_if_fail (filter->priv->child_model != NULL, FALSE); - - indices = gtk_tree_path_get_indices (path); - - if (filter->priv->root == NULL) - gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE); - level = FILTER_LEVEL (filter->priv->root); - - depth = gtk_tree_path_get_depth (path); - if (!depth) - { - iter->stamp = 0; - return FALSE; - } - - for (i = 0; i < depth - 1; i++) - { - if (!level || indices[i] >= g_sequence_get_length (level->visible_seq)) - { - iter->stamp = 0; - return FALSE; - } - - siter = g_sequence_get_iter_at_pos (level->visible_seq, indices[i]); - if (g_sequence_iter_is_end (siter)) - { - iter->stamp = 0; - return FALSE; - } - - elt = GET_ELT (siter); - g_assert (elt); - if (!elt->children) - gtk_tree_model_filter_build_level (filter, level, elt, FALSE); - level = elt->children; - } - - if (!level || indices[i] >= g_sequence_get_length (level->visible_seq)) - { - iter->stamp = 0; - return FALSE; - } - - iter->stamp = filter->priv->stamp; - iter->user_data = level; - - siter = g_sequence_get_iter_at_pos (level->visible_seq, indices[depth - 1]); - if (g_sequence_iter_is_end (siter)) - { - iter->stamp = 0; - return FALSE; - } - iter->user_data2 = GET_ELT (siter); - - return TRUE; -} - -static GtkTreePath * -gtk_tree_model_filter_get_path (GtkTreeModel *model, - GtkTreeIter *iter) -{ - GtkTreePath *retval; - FilterLevel *level; - FilterElt *elt; - - g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), NULL); - g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->child_model != NULL, NULL); - g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->stamp == iter->stamp, NULL); - - level = iter->user_data; - elt = iter->user_data2; - - if (!elt->visible_siter) - return NULL; - - retval = gtk_tree_path_new (); - - while (level) - { - int index; - - index = g_sequence_iter_get_position (elt->visible_siter); - gtk_tree_path_prepend_index (retval, index); - - elt = level->parent_elt; - level = level->parent_level; - } - - return retval; -} - -static void -gtk_tree_model_filter_real_modify (GtkTreeModelFilter *self, - GtkTreeModel *child_model, - GtkTreeIter *iter, - GValue *value, - int column) -{ - if (self->priv->modify_func) - { - g_return_if_fail (column < self->priv->modify_n_columns); - - g_value_init (value, self->priv->modify_types[column]); - self->priv->modify_func (GTK_TREE_MODEL (self), - iter, value, column, - self->priv->modify_data); - } - else - { - GtkTreeIter child_iter; - - gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (self), - &child_iter, iter); - gtk_tree_model_get_value (child_model, &child_iter, column, value); - } -} - -static void -gtk_tree_model_filter_get_value (GtkTreeModel *model, - GtkTreeIter *iter, - int column, - GValue *value) -{ - GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (model); - - g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (model)); - g_return_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->child_model != NULL); - g_return_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->stamp == iter->stamp); - - GTK_TREE_MODEL_FILTER_GET_CLASS (model)->modify (filter, - filter->priv->child_model, iter, value, column); -} - -static gboolean -gtk_tree_model_filter_iter_next (GtkTreeModel *model, - GtkTreeIter *iter) -{ - FilterElt *elt; - GSequenceIter *siter; - - g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), FALSE); - g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->child_model != NULL, FALSE); - g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->stamp == iter->stamp, FALSE); - - elt = iter->user_data2; - - siter = g_sequence_iter_next (elt->visible_siter); - if (g_sequence_iter_is_end (siter)) - { - iter->stamp = 0; - return FALSE; - } - - iter->user_data2 = GET_ELT (siter); - - return TRUE; -} - -static gboolean -gtk_tree_model_filter_iter_previous (GtkTreeModel *model, - GtkTreeIter *iter) -{ - FilterElt *elt; - GSequenceIter *siter; - - g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), FALSE); - g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->child_model != NULL, FALSE); - g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->stamp == iter->stamp, FALSE); - - elt = iter->user_data2; - - if (g_sequence_iter_is_begin (elt->visible_siter)) - { - iter->stamp = 0; - return FALSE; - } - siter = g_sequence_iter_prev (elt->visible_siter); - - iter->user_data2 = GET_ELT (siter); - - return TRUE; -} - -static gboolean -gtk_tree_model_filter_iter_children (GtkTreeModel *model, - GtkTreeIter *iter, - GtkTreeIter *parent) -{ - GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model; - FilterLevel *level; - GSequenceIter *siter; - - iter->stamp = 0; - g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), FALSE); - g_return_val_if_fail (filter->priv->child_model != NULL, FALSE); - if (parent) - g_return_val_if_fail (filter->priv->stamp == parent->stamp, FALSE); - - if (!parent) - { - if (!filter->priv->root) - gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE); - if (!filter->priv->root) - return FALSE; - - level = filter->priv->root; - siter = g_sequence_get_begin_iter (level->visible_seq); - if (g_sequence_iter_is_end (siter)) - { - iter->stamp = 0; - return FALSE; - } - - iter->stamp = filter->priv->stamp; - iter->user_data = level; - iter->user_data2 = GET_ELT (siter); - - return TRUE; - } - else - { - if (FILTER_ELT (parent->user_data2)->children == NULL) - gtk_tree_model_filter_build_level (filter, - FILTER_LEVEL (parent->user_data), - FILTER_ELT (parent->user_data2), - FALSE); - if (FILTER_ELT (parent->user_data2)->children == NULL) - return FALSE; - - level = FILTER_ELT (parent->user_data2)->children; - siter = g_sequence_get_begin_iter (level->visible_seq); - if (g_sequence_iter_is_end (siter)) - { - iter->stamp = 0; - return FALSE; - } - - iter->stamp = filter->priv->stamp; - iter->user_data = level; - iter->user_data2 = GET_ELT (siter); - - return TRUE; - } -} - -static gboolean -gtk_tree_model_filter_iter_has_child (GtkTreeModel *model, - GtkTreeIter *iter) -{ - GtkTreeIter child_iter; - GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model; - FilterElt *elt; - - g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), FALSE); - g_return_val_if_fail (filter->priv->child_model != NULL, FALSE); - g_return_val_if_fail (filter->priv->stamp == iter->stamp, FALSE); - - filter = GTK_TREE_MODEL_FILTER (model); - - gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_iter, iter); - elt = FILTER_ELT (iter->user_data2); - - if (!elt->visible_siter) - return FALSE; - - /* we need to build the level to check if not all children are filtered - * out - */ - if (!elt->children - && gtk_tree_model_iter_has_child (filter->priv->child_model, &child_iter)) - gtk_tree_model_filter_build_level (filter, FILTER_LEVEL (iter->user_data), - elt, FALSE); - - if (elt->children && g_sequence_get_length (elt->children->visible_seq) > 0) - return TRUE; - - return FALSE; -} - -static int -gtk_tree_model_filter_iter_n_children (GtkTreeModel *model, - GtkTreeIter *iter) -{ - GtkTreeIter child_iter; - GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model; - FilterElt *elt; - - g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), 0); - g_return_val_if_fail (filter->priv->child_model != NULL, 0); - if (iter) - g_return_val_if_fail (filter->priv->stamp == iter->stamp, 0); - - if (!iter) - { - if (!filter->priv->root) - gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE); - - if (filter->priv->root) - return g_sequence_get_length (FILTER_LEVEL (filter->priv->root)->visible_seq); - - return 0; - } - - elt = FILTER_ELT (iter->user_data2); - - if (!elt->visible_siter) - return 0; - - gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_iter, iter); - - if (!elt->children && - gtk_tree_model_iter_has_child (filter->priv->child_model, &child_iter)) - gtk_tree_model_filter_build_level (filter, - FILTER_LEVEL (iter->user_data), - elt, FALSE); - - if (elt->children) - return g_sequence_get_length (elt->children->visible_seq); - - return 0; -} - -static gboolean -gtk_tree_model_filter_iter_nth_child (GtkTreeModel *model, - GtkTreeIter *iter, - GtkTreeIter *parent, - int n) -{ - FilterLevel *level; - GtkTreeIter children; - GSequenceIter *siter; - - g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), FALSE); - if (parent) - g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->stamp == parent->stamp, FALSE); - - /* use this instead of has_child to force us to build the level, if needed */ - if (gtk_tree_model_filter_iter_children (model, &children, parent) == FALSE) - { - iter->stamp = 0; - return FALSE; - } - - level = children.user_data; - siter = g_sequence_get_iter_at_pos (level->visible_seq, n); - if (g_sequence_iter_is_end (siter)) - return FALSE; - - iter->stamp = GTK_TREE_MODEL_FILTER (model)->priv->stamp; - iter->user_data = level; - iter->user_data2 = GET_ELT (siter); - - return TRUE; -} - -static gboolean -gtk_tree_model_filter_iter_parent (GtkTreeModel *model, - GtkTreeIter *iter, - GtkTreeIter *child) -{ - FilterLevel *level; - - iter->stamp = 0; - g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), FALSE); - g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->child_model != NULL, FALSE); - g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->stamp == child->stamp, FALSE); - - level = child->user_data; - - if (level->parent_level) - { - iter->stamp = GTK_TREE_MODEL_FILTER (model)->priv->stamp; - iter->user_data = level->parent_level; - iter->user_data2 = level->parent_elt; - - return TRUE; - } - - return FALSE; -} - -static void -gtk_tree_model_filter_ref_node (GtkTreeModel *model, - GtkTreeIter *iter) -{ - gtk_tree_model_filter_real_ref_node (model, iter, TRUE); -} - -static void -gtk_tree_model_filter_real_ref_node (GtkTreeModel *model, - GtkTreeIter *iter, - gboolean external) -{ - GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model; - GtkTreeIter child_iter; - FilterLevel *level; - FilterElt *elt; - - g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (model)); - g_return_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->child_model != NULL); - g_return_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->stamp == iter->stamp); - - gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_iter, iter); - - gtk_tree_model_ref_node (filter->priv->child_model, &child_iter); - - level = iter->user_data; - elt = iter->user_data2; - - elt->ref_count++; - level->ref_count++; - - if (external) - { - elt->ext_ref_count++; - level->ext_ref_count++; - - if (level->ext_ref_count == 1) - { - FilterLevel *parent_level = level->parent_level; - FilterElt *parent_elt = level->parent_elt; - - /* we were at zero -- time to decrease the zero_ref_count val */ - while (parent_level) - { - parent_elt->zero_ref_count--; - - parent_elt = parent_level->parent_elt; - parent_level = parent_level->parent_level; - } - - if (filter->priv->root != level) - filter->priv->zero_ref_count--; - -#ifdef MODEL_FILTER_DEBUG - g_assert (filter->priv->zero_ref_count >= 0); - if (filter->priv->zero_ref_count > 0) - g_assert (filter->priv->root != NULL); -#endif - } - } - -#ifdef MODEL_FILTER_DEBUG - g_assert (elt->ref_count >= elt->ext_ref_count); - g_assert (elt->ref_count >= 0); - g_assert (elt->ext_ref_count >= 0); -#endif -} - -static void -gtk_tree_model_filter_unref_node (GtkTreeModel *model, - GtkTreeIter *iter) -{ - gtk_tree_model_filter_real_unref_node (model, iter, TRUE, TRUE); -} - -static void -gtk_tree_model_filter_real_unref_node (GtkTreeModel *model, - GtkTreeIter *iter, - gboolean external, - gboolean propagate_unref) -{ - GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model; - FilterLevel *level; - FilterElt *elt; - - g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (model)); - g_return_if_fail (filter->priv->child_model != NULL); - g_return_if_fail (filter->priv->stamp == iter->stamp); - - if (propagate_unref) - { - GtkTreeIter child_iter; - gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_iter, iter); - gtk_tree_model_unref_node (filter->priv->child_model, &child_iter); - } - - level = iter->user_data; - elt = iter->user_data2; - - g_return_if_fail (elt->ref_count > 0); -#ifdef MODEL_FILTER_DEBUG - g_assert (elt->ref_count >= elt->ext_ref_count); - g_assert (elt->ref_count >= 0); - g_assert (elt->ext_ref_count >= 0); -#endif - - elt->ref_count--; - level->ref_count--; - - if (external) - { - elt->ext_ref_count--; - level->ext_ref_count--; - - if (level->ext_ref_count == 0) - { - FilterLevel *parent_level = level->parent_level; - FilterElt *parent_elt = level->parent_elt; - - /* we are at zero -- time to increase the zero_ref_count val */ - while (parent_level) - { - parent_elt->zero_ref_count++; - - parent_elt = parent_level->parent_elt; - parent_level = parent_level->parent_level; - } - - if (filter->priv->root != level) - filter->priv->zero_ref_count++; - -#ifdef MODEL_FILTER_DEBUG - g_assert (filter->priv->zero_ref_count >= 0); - if (filter->priv->zero_ref_count > 0) - g_assert (filter->priv->root != NULL); -#endif - } - } - -#ifdef MODEL_FILTER_DEBUG - g_assert (elt->ref_count >= elt->ext_ref_count); - g_assert (elt->ref_count >= 0); - g_assert (elt->ext_ref_count >= 0); -#endif -} - -/* TreeDragSource interface implementation */ -static gboolean -gtk_tree_model_filter_row_draggable (GtkTreeDragSource *drag_source, - GtkTreePath *path) -{ - GtkTreeModelFilter *tree_model_filter = (GtkTreeModelFilter *)drag_source; - GtkTreePath *child_path; - gboolean draggable; - - g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (drag_source), FALSE); - g_return_val_if_fail (path != NULL, FALSE); - - child_path = gtk_tree_model_filter_convert_path_to_child_path (tree_model_filter, path); - draggable = gtk_tree_drag_source_row_draggable (GTK_TREE_DRAG_SOURCE (tree_model_filter->priv->child_model), child_path); - gtk_tree_path_free (child_path); - - return draggable; -} - -static GdkContentProvider * -gtk_tree_model_filter_drag_data_get (GtkTreeDragSource *drag_source, - GtkTreePath *path) -{ - GtkTreeModelFilter *tree_model_filter = (GtkTreeModelFilter *)drag_source; - GtkTreePath *child_path; - GdkContentProvider *gotten; - - g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (drag_source), NULL); - g_return_val_if_fail (path != NULL, NULL); - - child_path = gtk_tree_model_filter_convert_path_to_child_path (tree_model_filter, path); - gotten = gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (tree_model_filter->priv->child_model), child_path); - gtk_tree_path_free (child_path); - - return gotten; -} - -static gboolean -gtk_tree_model_filter_drag_data_delete (GtkTreeDragSource *drag_source, - GtkTreePath *path) -{ - GtkTreeModelFilter *tree_model_filter = (GtkTreeModelFilter *)drag_source; - GtkTreePath *child_path; - gboolean deleted; - - g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (drag_source), FALSE); - g_return_val_if_fail (path != NULL, FALSE); - - child_path = gtk_tree_model_filter_convert_path_to_child_path (tree_model_filter, path); - deleted = gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (tree_model_filter->priv->child_model), child_path); - gtk_tree_path_free (child_path); - - return deleted; -} - -/* bits and pieces */ -static void -gtk_tree_model_filter_set_model (GtkTreeModelFilter *filter, - GtkTreeModel *child_model) -{ - g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (filter)); - - if (filter->priv->child_model) - { - g_signal_handler_disconnect (filter->priv->child_model, - filter->priv->changed_id); - g_signal_handler_disconnect (filter->priv->child_model, - filter->priv->inserted_id); - g_signal_handler_disconnect (filter->priv->child_model, - filter->priv->has_child_toggled_id); - g_signal_handler_disconnect (filter->priv->child_model, - filter->priv->deleted_id); - g_signal_handler_disconnect (filter->priv->child_model, - filter->priv->reordered_id); - - /* reset our state */ - if (filter->priv->root) - gtk_tree_model_filter_free_level (filter, filter->priv->root, - TRUE, TRUE, FALSE); - - filter->priv->root = NULL; - g_object_unref (filter->priv->child_model); - filter->priv->visible_column = -1; - - /* FIXME: do we need to destroy more here? */ - } - - filter->priv->child_model = child_model; - - if (child_model) - { - g_object_ref (filter->priv->child_model); - filter->priv->changed_id = - g_signal_connect (child_model, "row-changed", - G_CALLBACK (gtk_tree_model_filter_row_changed), - filter); - filter->priv->inserted_id = - g_signal_connect (child_model, "row-inserted", - G_CALLBACK (gtk_tree_model_filter_row_inserted), - filter); - filter->priv->has_child_toggled_id = - g_signal_connect (child_model, "row-has-child-toggled", - G_CALLBACK (gtk_tree_model_filter_row_has_child_toggled), - filter); - filter->priv->deleted_id = - g_signal_connect (child_model, "row-deleted", - G_CALLBACK (gtk_tree_model_filter_row_deleted), - filter); - filter->priv->reordered_id = - g_signal_connect (child_model, "rows-reordered", - G_CALLBACK (gtk_tree_model_filter_rows_reordered), - filter); - - filter->priv->child_flags = gtk_tree_model_get_flags (child_model); - filter->priv->stamp = g_random_int (); - } -} - -static void -gtk_tree_model_filter_ref_path (GtkTreeModelFilter *filter, - GtkTreePath *path) -{ - int len; - GtkTreePath *p; - - len = gtk_tree_path_get_depth (path); - p = gtk_tree_path_copy (path); - while (len--) - { - GtkTreeIter iter; - - gtk_tree_model_get_iter (GTK_TREE_MODEL (filter->priv->child_model), &iter, p); - gtk_tree_model_ref_node (GTK_TREE_MODEL (filter->priv->child_model), &iter); - gtk_tree_path_up (p); - } - - gtk_tree_path_free (p); -} - -static void -gtk_tree_model_filter_unref_path (GtkTreeModelFilter *filter, - GtkTreePath *path, - int depth) -{ - int len; - GtkTreePath *p; - - if (depth != -1) - len = depth; - else - len = gtk_tree_path_get_depth (path); - - p = gtk_tree_path_copy (path); - while (len--) - { - GtkTreeIter iter; - - gtk_tree_model_get_iter (GTK_TREE_MODEL (filter->priv->child_model), &iter, p); - gtk_tree_model_unref_node (GTK_TREE_MODEL (filter->priv->child_model), &iter); - gtk_tree_path_up (p); - } - - gtk_tree_path_free (p); -} - -static void -gtk_tree_model_filter_set_root (GtkTreeModelFilter *filter, - GtkTreePath *root) -{ - g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (filter)); - - if (root) - { - filter->priv->virtual_root = gtk_tree_path_copy (root); - gtk_tree_model_filter_ref_path (filter, filter->priv->virtual_root); - filter->priv->virtual_root_deleted = FALSE; - } - else - filter->priv->virtual_root = NULL; -} - -/* public API */ - -/** - * gtk_tree_model_filter_new: - * @child_model: A `GtkTreeModel`. - * @root: (nullable): A `GtkTreePath` - * - * Creates a new `GtkTreeModel`, with @child_model as the child_model - * and @root as the virtual root. - * - * Returns: (transfer full): A new `GtkTreeModel`. - */ -GtkTreeModel * -gtk_tree_model_filter_new (GtkTreeModel *child_model, - GtkTreePath *root) -{ - g_return_val_if_fail (GTK_IS_TREE_MODEL (child_model), NULL); - - return g_object_new (GTK_TYPE_TREE_MODEL_FILTER, - "child-model", child_model, - "virtual-root", root, - NULL); -} - -/** - * gtk_tree_model_filter_get_model: - * @filter: A `GtkTreeModelFilter` - * - * Returns a pointer to the child model of @filter. - * - * Returns: (transfer none): A pointer to a `GtkTreeModel` - */ -GtkTreeModel * -gtk_tree_model_filter_get_model (GtkTreeModelFilter *filter) -{ - g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (filter), NULL); - - return filter->priv->child_model; -} - -/** - * gtk_tree_model_filter_set_visible_func: - * @filter: A `GtkTreeModelFilter` - * @func: A `GtkTreeModelFilterVisibleFunc`, the visible function - * @data: (nullable): User data to pass to the visible function - * @destroy: (nullable): Destroy notifier of @data - * - * Sets the visible function used when filtering the @filter to be @func. - * The function should return %TRUE if the given row should be visible and - * %FALSE otherwise. - * - * If the condition calculated by the function changes over time (e.g. - * because it depends on some global parameters), you must call - * gtk_tree_model_filter_refilter() to keep the visibility information - * of the model up-to-date. - * - * Note that @func is called whenever a row is inserted, when it may still - * be empty. The visible function should therefore take special care of empty - * rows, like in the example below. - * - * |[ - * static gboolean - * visible_func (GtkTreeModel *model, - * GtkTreeIter *iter, - * gpointer data) - * { - * // Visible if row is non-empty and first column is “HI” - * char *str; - * gboolean visible = FALSE; - * - * gtk_tree_model_get (model, iter, 0, &str, -1); - * if (str && strcmp (str, "HI") == 0) - * visible = TRUE; - * g_free (str); - * - * return visible; - * } - * ]| - * - * Note that gtk_tree_model_filter_set_visible_func() or - * gtk_tree_model_filter_set_visible_column() can only be called - * once for a given filter model. - */ -void -gtk_tree_model_filter_set_visible_func (GtkTreeModelFilter *filter, - GtkTreeModelFilterVisibleFunc func, - gpointer data, - GDestroyNotify destroy) -{ - g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (filter)); - g_return_if_fail (func != NULL); - g_return_if_fail (filter->priv->visible_method_set == FALSE); - - filter->priv->visible_func = func; - filter->priv->visible_data = data; - filter->priv->visible_destroy = destroy; - - filter->priv->visible_method_set = TRUE; -} - -/** - * gtk_tree_model_filter_set_modify_func: - * @filter: A `GtkTreeModelFilter` - * @n_columns: The number of columns in the filter model. - * @types: (array length=n_columns): The `GType`s of the columns. - * @func: A `GtkTreeModelFilterModifyFunc` - * @data: (nullable): User data to pass to the modify function - * @destroy: (nullable): Destroy notifier of @data - * - * With the @n_columns and @types parameters, you give an array of column - * types for this model (which will be exposed to the parent model/view). - * The @func, @data and @destroy parameters are for specifying the modify - * function. The modify function will get called for each - * data access, the goal of the modify function is to return the data which - * should be displayed at the location specified using the parameters of the - * modify function. - * - * Note that gtk_tree_model_filter_set_modify_func() - * can only be called once for a given filter model. - */ -void -gtk_tree_model_filter_set_modify_func (GtkTreeModelFilter *filter, - int n_columns, - GType *types, - GtkTreeModelFilterModifyFunc func, - gpointer data, - GDestroyNotify destroy) -{ - g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (filter)); - g_return_if_fail (func != NULL); - g_return_if_fail (filter->priv->modify_func_set == FALSE); - - filter->priv->modify_n_columns = n_columns; - filter->priv->modify_types = g_new0 (GType, n_columns); - memcpy (filter->priv->modify_types, types, sizeof (GType) * n_columns); - filter->priv->modify_func = func; - filter->priv->modify_data = data; - filter->priv->modify_destroy = destroy; - - filter->priv->modify_func_set = TRUE; -} - -/** - * gtk_tree_model_filter_set_visible_column: - * @filter: A `GtkTreeModelFilter` - * @column: A `int` which is the column containing the visible information - * - * Sets @column of the child_model to be the column where @filter should - * look for visibility information. @columns should be a column of type - * %G_TYPE_BOOLEAN, where %TRUE means that a row is visible, and %FALSE - * if not. - * - * Note that gtk_tree_model_filter_set_visible_func() or - * gtk_tree_model_filter_set_visible_column() can only be called - * once for a given filter model. - */ -void -gtk_tree_model_filter_set_visible_column (GtkTreeModelFilter *filter, - int column) -{ - g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (filter)); - g_return_if_fail (column >= 0); - g_return_if_fail (filter->priv->visible_method_set == FALSE); - - filter->priv->visible_column = column; - - filter->priv->visible_method_set = TRUE; -} - -/* conversion */ - -/** - * gtk_tree_model_filter_convert_child_iter_to_iter: - * @filter: A `GtkTreeModelFilter` - * @filter_iter: (out): An uninitialized `GtkTreeIter` - * @child_iter: A valid `GtkTreeIter` pointing to a row on the child model. - * - * Sets @filter_iter to point to the row in @filter that corresponds to the - * row pointed at by @child_iter. If @filter_iter was not set, %FALSE is - * returned. - * - * Returns: %TRUE, if @filter_iter was set, i.e. if @child_iter is a - * valid iterator pointing to a visible row in child model. - */ -gboolean -gtk_tree_model_filter_convert_child_iter_to_iter (GtkTreeModelFilter *filter, - GtkTreeIter *filter_iter, - GtkTreeIter *child_iter) -{ - gboolean ret; - GtkTreePath *child_path, *path; - - g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (filter), FALSE); - g_return_val_if_fail (filter->priv->child_model != NULL, FALSE); - g_return_val_if_fail (filter_iter != NULL, FALSE); - g_return_val_if_fail (child_iter != NULL, FALSE); - g_return_val_if_fail (filter_iter != child_iter, FALSE); - - filter_iter->stamp = 0; - - child_path = gtk_tree_model_get_path (filter->priv->child_model, child_iter); - g_return_val_if_fail (child_path != NULL, FALSE); - - path = gtk_tree_model_filter_convert_child_path_to_path (filter, - child_path); - gtk_tree_path_free (child_path); - - if (!path) - return FALSE; - - ret = gtk_tree_model_get_iter (GTK_TREE_MODEL (filter), filter_iter, path); - gtk_tree_path_free (path); - - return ret; -} - -/** - * gtk_tree_model_filter_convert_iter_to_child_iter: - * @filter: A `GtkTreeModelFilter` - * @child_iter: (out): An uninitialized `GtkTreeIter` - * @filter_iter: A valid `GtkTreeIter` pointing to a row on @filter. - * - * Sets @child_iter to point to the row pointed to by @filter_iter. - */ -void -gtk_tree_model_filter_convert_iter_to_child_iter (GtkTreeModelFilter *filter, - GtkTreeIter *child_iter, - GtkTreeIter *filter_iter) -{ - g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (filter)); - g_return_if_fail (filter->priv->child_model != NULL); - g_return_if_fail (child_iter != NULL); - g_return_if_fail (filter_iter != NULL); - g_return_if_fail (filter_iter->stamp == filter->priv->stamp); - g_return_if_fail (filter_iter != child_iter); - - if (GTK_TREE_MODEL_FILTER_CACHE_CHILD_ITERS (filter)) - { - *child_iter = FILTER_ELT (filter_iter->user_data2)->iter; - } - else - { - GtkTreePath *path; - gboolean valid = FALSE; - - path = gtk_tree_model_filter_elt_get_path (filter_iter->user_data, - filter_iter->user_data2, - filter->priv->virtual_root); - valid = gtk_tree_model_get_iter (filter->priv->child_model, child_iter, - path); - gtk_tree_path_free (path); - - g_return_if_fail (valid == TRUE); - } -} - -/* The path returned can only be used internally in the filter model. */ -static GtkTreePath * -gtk_real_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter *filter, - GtkTreePath *child_path, - gboolean build_levels, - gboolean fetch_children) -{ - int *child_indices; - GtkTreePath *retval; - GtkTreePath *real_path; - FilterLevel *level; - FilterElt *tmp; - int i; - - g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (filter), NULL); - g_return_val_if_fail (filter->priv->child_model != NULL, NULL); - g_return_val_if_fail (child_path != NULL, NULL); - - if (!filter->priv->virtual_root) - real_path = gtk_tree_path_copy (child_path); - else - real_path = gtk_tree_model_filter_remove_root (child_path, - filter->priv->virtual_root); - - if (!real_path) - return NULL; - - retval = gtk_tree_path_new (); - child_indices = gtk_tree_path_get_indices (real_path); - - if (filter->priv->root == NULL && build_levels) - gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE); - level = FILTER_LEVEL (filter->priv->root); - - for (i = 0; i < gtk_tree_path_get_depth (real_path); i++) - { - GSequenceIter *siter; - gboolean found_child = FALSE; - - if (!level) - { - gtk_tree_path_free (real_path); - gtk_tree_path_free (retval); - return NULL; - } - - tmp = lookup_elt_with_offset (level->seq, child_indices[i], &siter); - if (tmp) - { - gtk_tree_path_append_index (retval, g_sequence_iter_get_position (siter)); - if (!tmp->children && build_levels) - gtk_tree_model_filter_build_level (filter, level, tmp, FALSE); - level = tmp->children; - found_child = TRUE; - } - - if (!found_child && fetch_children) - { - int j; - - tmp = gtk_tree_model_filter_fetch_child (filter, level, - child_indices[i], - &j); - - /* didn't find the child, let's try to bring it back */ - if (!tmp || tmp->offset != child_indices[i]) - { - /* not there */ - gtk_tree_path_free (real_path); - gtk_tree_path_free (retval); - return NULL; - } - - gtk_tree_path_append_index (retval, j); - if (!tmp->children && build_levels) - gtk_tree_model_filter_build_level (filter, level, tmp, FALSE); - level = tmp->children; - found_child = TRUE; - } - else if (!found_child && !fetch_children) - { - /* no path */ - gtk_tree_path_free (real_path); - gtk_tree_path_free (retval); - return NULL; - } - } - - gtk_tree_path_free (real_path); - return retval; -} - -/** - * gtk_tree_model_filter_convert_child_path_to_path: - * @filter: A `GtkTreeModelFilter` - * @child_path: A `GtkTreePath` to convert. - * - * Converts @child_path to a path relative to @filter. That is, @child_path - * points to a path in the child model. The rerturned path will point to the - * same row in the filtered model. If @child_path isn’t a valid path on the - * child model or points to a row which is not visible in @filter, then %NULL - * is returned. - * - * Returns: (nullable) (transfer full): A newly allocated `GtkTreePath` - */ -GtkTreePath * -gtk_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter *filter, - GtkTreePath *child_path) -{ - GtkTreeIter iter; - GtkTreePath *path; - - /* this function does the sanity checks */ - path = gtk_real_tree_model_filter_convert_child_path_to_path (filter, - child_path, - TRUE, - TRUE); - - if (!path) - return NULL; - - /* get a new path which only takes visible nodes into account. - * -- if this gives any performance issues, we can write a special - * version of convert_child_path_to_path immediately returning - * a visible-nodes-only path. - */ - gtk_tree_model_filter_get_iter_full (GTK_TREE_MODEL (filter), &iter, path); - - gtk_tree_path_free (path); - path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter); - - return path; -} - -/** - * gtk_tree_model_filter_convert_path_to_child_path: - * @filter: A `GtkTreeModelFilter` - * @filter_path: A `GtkTreePath` to convert. - * - * Converts @filter_path to a path on the child model of @filter. That is, - * @filter_path points to a location in @filter. The returned path will - * point to the same location in the model not being filtered. If @filter_path - * does not point to a location in the child model, %NULL is returned. - * - * Returns: (nullable) (transfer full): A newly allocated `GtkTreePath` - */ -GtkTreePath * -gtk_tree_model_filter_convert_path_to_child_path (GtkTreeModelFilter *filter, - GtkTreePath *filter_path) -{ - int *filter_indices; - GtkTreePath *retval; - FilterLevel *level; - int i; - - g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (filter), NULL); - g_return_val_if_fail (filter->priv->child_model != NULL, NULL); - g_return_val_if_fail (filter_path != NULL, NULL); - - /* convert path */ - retval = gtk_tree_path_new (); - filter_indices = gtk_tree_path_get_indices (filter_path); - if (!filter->priv->root) - gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE); - level = FILTER_LEVEL (filter->priv->root); - - for (i = 0; i < gtk_tree_path_get_depth (filter_path); i++) - { - FilterElt *elt; - GSequenceIter *siter; - - if (!level) - { - gtk_tree_path_free (retval); - return NULL; - } - - siter = g_sequence_get_iter_at_pos (level->visible_seq, filter_indices[i]); - if (g_sequence_iter_is_end (siter)) - { - gtk_tree_path_free (retval); - return NULL; - } - - elt = GET_ELT (siter); - g_assert (elt); - if (elt->children == NULL) - gtk_tree_model_filter_build_level (filter, level, elt, FALSE); - - gtk_tree_path_append_index (retval, elt->offset); - level = elt->children; - } - - /* apply vroot */ - - if (filter->priv->virtual_root) - { - GtkTreePath *real_retval; - - real_retval = gtk_tree_model_filter_add_root (retval, - filter->priv->virtual_root); - gtk_tree_path_free (retval); - - return real_retval; - } - - return retval; -} - -static gboolean -gtk_tree_model_filter_refilter_helper (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data) -{ - /* evil, don't try this at home, but certainly speeds things up */ - gtk_tree_model_filter_row_changed (model, path, iter, data); - - return FALSE; -} - -/** - * gtk_tree_model_filter_refilter: - * @filter: A `GtkTreeModelFilter` - * - * Emits ::row_changed for each row in the child model, which causes - * the filter to re-evaluate whether a row is visible or not. - */ -void -gtk_tree_model_filter_refilter (GtkTreeModelFilter *filter) -{ - g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (filter)); - - /* S L O W */ - gtk_tree_model_foreach (filter->priv->child_model, - gtk_tree_model_filter_refilter_helper, - filter); -} - -/** - * gtk_tree_model_filter_clear_cache: - * @filter: A `GtkTreeModelFilter` - * - * This function should almost never be called. It clears the @filter - * of any cached iterators that haven’t been reffed with - * gtk_tree_model_ref_node(). This might be useful if the child model - * being filtered is static (and doesn’t change often) and there has been - * a lot of unreffed access to nodes. As a side effect of this function, - * all unreffed iters will be invalid. - */ -void -gtk_tree_model_filter_clear_cache (GtkTreeModelFilter *filter) -{ - g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (filter)); - - if (filter->priv->zero_ref_count > 0) - gtk_tree_model_filter_clear_cache_helper (filter, - FILTER_LEVEL (filter->priv->root)); -} diff --git a/gtk/gtktreemodelfilter.h b/gtk/gtktreemodelfilter.h deleted file mode 100644 index bad3e08cb5..0000000000 --- a/gtk/gtktreemodelfilter.h +++ /dev/null @@ -1,156 +0,0 @@ -/* gtktreemodelfilter.h - * Copyright (C) 2000,2001 Red Hat, Inc., Jonathan Blandford - * Copyright (C) 2001-2003 Kristian Rietveld - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#ifndef __GTK_TREE_MODEL_FILTER_H__ -#define __GTK_TREE_MODEL_FILTER_H__ - -#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) -#error "Only can be included directly." -#endif - -#include - -G_BEGIN_DECLS - -#define GTK_TYPE_TREE_MODEL_FILTER (gtk_tree_model_filter_get_type ()) -#define GTK_TREE_MODEL_FILTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_TREE_MODEL_FILTER, GtkTreeModelFilter)) -#define GTK_TREE_MODEL_FILTER_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST ((vtable), GTK_TYPE_TREE_MODEL_FILTER, GtkTreeModelFilterClass)) -#define GTK_IS_TREE_MODEL_FILTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_TREE_MODEL_FILTER)) -#define GTK_IS_TREE_MODEL_FILTER_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), GTK_TYPE_TREE_MODEL_FILTER)) -#define GTK_TREE_MODEL_FILTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_TREE_MODEL_FILTER, GtkTreeModelFilterClass)) - -/** - * GtkTreeModelFilterVisibleFunc: - * @model: the child model of the `GtkTreeModelFilter` - * @iter: a `GtkTreeIter` pointing to the row in @model whose visibility - * is determined - * @data: (closure): user data given to gtk_tree_model_filter_set_visible_func() - * - * A function which decides whether the row indicated by @iter is visible. - * - * Returns: Whether the row indicated by @iter is visible. - */ -typedef gboolean (* GtkTreeModelFilterVisibleFunc) (GtkTreeModel *model, - GtkTreeIter *iter, - gpointer data); - -/** - * GtkTreeModelFilterModifyFunc: - * @model: the `GtkTreeModelFilter` - * @iter: a `GtkTreeIter` pointing to the row whose display values are determined - * @value: (out caller-allocates): A `GValue` which is already initialized for - * with the correct type for the column @column. - * @column: the column whose display value is determined - * @data: (closure): user data given to gtk_tree_model_filter_set_modify_func() - * - * A function which calculates display values from raw values in the model. - * It must fill @value with the display value for the column @column in the - * row indicated by @iter. - * - * Since this function is called for each data access, it’s not a - * particularly efficient operation. - */ - -typedef void (* GtkTreeModelFilterModifyFunc) (GtkTreeModel *model, - GtkTreeIter *iter, - GValue *value, - int column, - gpointer data); - -typedef struct _GtkTreeModelFilter GtkTreeModelFilter; -typedef struct _GtkTreeModelFilterClass GtkTreeModelFilterClass; -typedef struct _GtkTreeModelFilterPrivate GtkTreeModelFilterPrivate; - -struct _GtkTreeModelFilter -{ - GObject parent; - - /*< private >*/ - GtkTreeModelFilterPrivate *priv; -}; - -struct _GtkTreeModelFilterClass -{ - GObjectClass parent_class; - - gboolean (* visible) (GtkTreeModelFilter *self, - GtkTreeModel *child_model, - GtkTreeIter *iter); - void (* modify) (GtkTreeModelFilter *self, - GtkTreeModel *child_model, - GtkTreeIter *iter, - GValue *value, - int column); - - /*< private >*/ - - gpointer padding[8]; -}; - -/* base */ -GDK_AVAILABLE_IN_ALL -GType gtk_tree_model_filter_get_type (void) G_GNUC_CONST; -GDK_AVAILABLE_IN_ALL -GtkTreeModel *gtk_tree_model_filter_new (GtkTreeModel *child_model, - GtkTreePath *root); -GDK_AVAILABLE_IN_ALL -void gtk_tree_model_filter_set_visible_func (GtkTreeModelFilter *filter, - GtkTreeModelFilterVisibleFunc func, - gpointer data, - GDestroyNotify destroy); -GDK_AVAILABLE_IN_ALL -void gtk_tree_model_filter_set_modify_func (GtkTreeModelFilter *filter, - int n_columns, - GType *types, - GtkTreeModelFilterModifyFunc func, - gpointer data, - GDestroyNotify destroy); -GDK_AVAILABLE_IN_ALL -void gtk_tree_model_filter_set_visible_column (GtkTreeModelFilter *filter, - int column); - -GDK_AVAILABLE_IN_ALL -GtkTreeModel *gtk_tree_model_filter_get_model (GtkTreeModelFilter *filter); - -/* conversion */ -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_model_filter_convert_child_iter_to_iter (GtkTreeModelFilter *filter, - GtkTreeIter *filter_iter, - GtkTreeIter *child_iter); -GDK_AVAILABLE_IN_ALL -void gtk_tree_model_filter_convert_iter_to_child_iter (GtkTreeModelFilter *filter, - GtkTreeIter *child_iter, - GtkTreeIter *filter_iter); -GDK_AVAILABLE_IN_ALL -GtkTreePath *gtk_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter *filter, - GtkTreePath *child_path); -GDK_AVAILABLE_IN_ALL -GtkTreePath *gtk_tree_model_filter_convert_path_to_child_path (GtkTreeModelFilter *filter, - GtkTreePath *filter_path); - -/* extras */ -GDK_AVAILABLE_IN_ALL -void gtk_tree_model_filter_refilter (GtkTreeModelFilter *filter); -GDK_AVAILABLE_IN_ALL -void gtk_tree_model_filter_clear_cache (GtkTreeModelFilter *filter); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkTreeModelFilter, g_object_unref) - -G_END_DECLS - -#endif /* __GTK_TREE_MODEL_FILTER_H__ */ diff --git a/gtk/gtktreemodelsort.c b/gtk/gtktreemodelsort.c deleted file mode 100644 index 0d800da62d..0000000000 --- a/gtk/gtktreemodelsort.c +++ /dev/null @@ -1,2779 +0,0 @@ -/* gtktreemodelsort.c - * Copyright (C) 2000,2001 Red Hat, Inc., Jonathan Blandford - * Copyright (C) 2001,2002 Kristian Rietveld - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#include "config.h" -#include - -#include "gtktreemodelsort.h" -#include "gtktreesortable.h" -#include "gtktreestore.h" -#include "gtktreedatalistprivate.h" -#include "gtkprivate.h" -#include "gtktreednd.h" - - -/** - * GtkTreeModelSort: - * - * A GtkTreeModel which makes an underlying tree model sortable - * - * The `GtkTreeModelSort` is a model which implements the `GtkTreeSortable` - * interface. It does not hold any data itself, but rather is created with - * a child model and proxies its data. It has identical column types to - * this child model, and the changes in the child are propagated. The - * primary purpose of this model is to provide a way to sort a different - * model without modifying it. Note that the sort function used by - * `GtkTreeModelSort` is not guaranteed to be stable. - * - * The use of this is best demonstrated through an example. In the - * following sample code we create two `GtkTreeView` widgets each with a - * view of the same data. As the model is wrapped here by a - * `GtkTreeModelSort`, the two `GtkTreeView`s can each sort their - * view of the data without affecting the other. By contrast, if we - * simply put the same model in each widget, then sorting the first would - * sort the second. - * - * ## Using a `GtkTreeModelSort` - * - * |[ - * { - * GtkTreeView *tree_view1; - * GtkTreeView *tree_view2; - * GtkTreeModel *sort_model1; - * GtkTreeModel *sort_model2; - * GtkTreeModel *child_model; - * - * // get the child model - * child_model = get_my_model (); - * - * // Create the first tree - * sort_model1 = gtk_tree_model_sort_new_with_model (child_model); - * tree_view1 = gtk_tree_view_new_with_model (sort_model1); - * - * // Create the second tree - * sort_model2 = gtk_tree_model_sort_new_with_model (child_model); - * tree_view2 = gtk_tree_view_new_with_model (sort_model2); - * - * // Now we can sort the two models independently - * gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort_model1), - * COLUMN_1, GTK_SORT_ASCENDING); - * gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort_model2), - * COLUMN_1, GTK_SORT_DESCENDING); - * } - * ]| - * - * To demonstrate how to access the underlying child model from the sort - * model, the next example will be a callback for the `GtkTreeSelection` - * `GtkTreeSelection::changed` signal. In this callback, we get a string - * from COLUMN_1 of the model. We then modify the string, find the same - * selected row on the child model, and change the row there. - * - * ## Accessing the child model of in a selection changed callback - * - * |[ - * void - * selection_changed (GtkTreeSelection *selection, gpointer data) - * { - * GtkTreeModel *sort_model = NULL; - * GtkTreeModel *child_model; - * GtkTreeIter sort_iter; - * GtkTreeIter child_iter; - * char *some_data = NULL; - * char *modified_data; - * - * // Get the current selected row and the model. - * if (! gtk_tree_selection_get_selected (selection, - * &sort_model, - * &sort_iter)) - * return; - * - * // Look up the current value on the selected row and get - * // a new value to change it to. - * gtk_tree_model_get (GTK_TREE_MODEL (sort_model), &sort_iter, - * COLUMN_1, &some_data, - * -1); - * - * modified_data = change_the_data (some_data); - * g_free (some_data); - * - * // Get an iterator on the child model, instead of the sort model. - * gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (sort_model), - * &child_iter, - * &sort_iter); - * - * // Get the child model and change the value of the row. In this - * // example, the child model is a GtkListStore. It could be any other - * // type of model, though. - * child_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model)); - * gtk_list_store_set (GTK_LIST_STORE (child_model), &child_iter, - * COLUMN_1, &modified_data, - * -1); - * g_free (modified_data); - * } - * ]| - */ - - -/* Notes on this implementation of GtkTreeModelSort - * ================================================ - * - * Warnings - * -------- - * - * In this code there is a potential for confusion as to whether an iter, - * path or value refers to the GtkTreeModelSort model, or to the child model - * that has been set. As a convention, variables referencing the child model - * will have an s_ prefix before them (ie. s_iter, s_value, s_path); - * Conversion of iterators and paths between GtkTreeModelSort and the child - * model is done through the various gtk_tree_model_sort_convert_* functions. - * - * Iterator format - * --------------- - * - * The iterator format of iterators handed out by GtkTreeModelSort is as - * follows: - * - * iter->stamp = tree_model_sort->stamp - * iter->user_data = SortLevel - * iter->user_data2 = SortElt - * - * Internal data structure - * ----------------------- - * - * Using SortLevel and SortElt, GtkTreeModelSort maintains a “cache” of - * the mapping from GtkTreeModelSort nodes to nodes in the child model. - * This is to avoid sorting a level each time an operation is requested - * on GtkTreeModelSort, such as get iter, get path, get value. - * - * A SortElt corresponds to a single node. A node and its siblings are - * stored in a SortLevel. The SortLevel keeps a reference to the parent - * SortElt and its SortLevel (if any). The SortElt can have a "children" - * pointer set, which points at a child level (a sub level). - * - * In a SortLevel, nodes are stored in a GSequence. The GSequence - * allows for fast additions and removals, and supports sorting - * the level of SortElt nodes. - * - * It is important to recognize the two different mappings that play - * a part in this code: - * I. The mapping from the client to this model. The order in which - * nodes are stored in the GSequence is the order in which the - * nodes are exposed to clients of the GtkTreeModelSort. - * II. The mapping from this model to its child model. Each SortElt - * contains an “offset” field which is the offset of the - * corresponding node in the child model. - * - * Reference counting - * ------------------ - * - * GtkTreeModelSort forwards all reference and unreference operations - * to the corresponding node in the child model. The reference count - * of each node is also maintained internally, in the “ref_count” - * fields in SortElt and SortLevel. For each ref and unref operation on - * a SortElt, the “ref_count” of the SortLevel is updated accordingly. - * In addition, if a SortLevel has a parent, a reference is taken on - * this parent. This happens in gtk_tree_model_sort_build_level() and - * the reference is released again in gtk_tree_model_sort_free_level(). - * This ensures that when GtkTreeModelSort takes a reference on a node - * (for example during sorting), all parent nodes are referenced - * according to reference counting rule 1, see the GtkTreeModel - * documentation. - * - * When a level has a reference count of zero, which means that - * none of the nodes in the level is referenced, the level has - * a “zero ref count” on all its parents. As soon as the level - * reaches a reference count of zero, the zero ref count value is - * incremented by one on all parents of this level. Similarly, as - * soon as the reference count of a level changes from zero, the - * zero ref count value is decremented by one on all parents. - * - * The zero ref count value is used to clear unused portions of - * the cache. If a SortElt has a zero ref count of one, then - * its child level is unused and can be removed from the cache. - * If the zero ref count value is higher than one, then the - * child level contains sublevels which are unused as well. - * gtk_tree_model_sort_clear_cache() uses this to not recurse - * into levels which have a zero ref count of zero. - */ - -typedef struct _SortElt SortElt; -typedef struct _SortLevel SortLevel; -typedef struct _SortData SortData; - -struct _SortElt -{ - GtkTreeIter iter; - SortLevel *children; - int offset; - int ref_count; - int zero_ref_count; - int old_index; /* used while sorting */ - GSequenceIter *siter; /* iter into seq */ -}; - -struct _SortLevel -{ - GSequence *seq; - int ref_count; - SortElt *parent_elt; - SortLevel *parent_level; -}; - -struct _SortData -{ - GtkTreeModelSort *tree_model_sort; - GtkTreeIterCompareFunc sort_func; - gpointer sort_data; - - GtkTreePath *parent_path; - int *parent_path_indices; - int parent_path_depth; -}; - -/* Properties */ -enum { - PROP_0, - /* Construct args */ - PROP_MODEL -}; - - -struct _GtkTreeModelSortPrivate -{ - gpointer root; - int stamp; - guint child_flags; - GtkTreeModel *child_model; - int zero_ref_count; - - /* sort information */ - GList *sort_list; - int sort_column_id; - GtkSortType order; - - /* default sort */ - GtkTreeIterCompareFunc default_sort_func; - gpointer default_sort_data; - GDestroyNotify default_sort_destroy; - - /* signal ids */ - gulong changed_id; - gulong inserted_id; - gulong has_child_toggled_id; - gulong deleted_id; - gulong reordered_id; -}; - -/* Set this to 0 to disable caching of child iterators. This - * allows for more stringent testing. It is recommended to set this - * to one when refactoring this code and running the unit tests to - * catch more errors. - */ -#if 1 -# define GTK_TREE_MODEL_SORT_CACHE_CHILD_ITERS(tree_model_sort) \ - (((GtkTreeModelSort *)tree_model_sort)->priv->child_flags>K_TREE_MODEL_ITERS_PERSIST) -#else -# define GTK_TREE_MODEL_SORT_CACHE_CHILD_ITERS(tree_model_sort) (FALSE) -#endif - -#define SORT_ELT(sort_elt) ((SortElt *)sort_elt) -#define SORT_LEVEL(sort_level) ((SortLevel *)sort_level) -#define GET_ELT(siter) ((SortElt *) (siter ? g_sequence_get (siter) : NULL)) - - -#define GET_CHILD_ITER(tree_model_sort,ch_iter,so_iter) gtk_tree_model_sort_convert_iter_to_child_iter((GtkTreeModelSort*)(tree_model_sort), (ch_iter), (so_iter)); - -#define NO_SORT_FUNC ((GtkTreeIterCompareFunc) 0x1) - -#define VALID_ITER(iter, tree_model_sort) ((iter) != NULL && (iter)->user_data != NULL && (iter)->user_data2 != NULL && (tree_model_sort)->priv->stamp == (iter)->stamp) - -/* general (object/interface init, etc) */ -static void gtk_tree_model_sort_tree_model_init (GtkTreeModelIface *iface); -static void gtk_tree_model_sort_tree_sortable_init (GtkTreeSortableIface *iface); -static void gtk_tree_model_sort_drag_source_init (GtkTreeDragSourceIface*iface); -static void gtk_tree_model_sort_finalize (GObject *object); -static void gtk_tree_model_sort_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec); -static void gtk_tree_model_sort_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec); - -/* our signal handlers */ -static void gtk_tree_model_sort_row_changed (GtkTreeModel *model, - GtkTreePath *start_path, - GtkTreeIter *start_iter, - gpointer data); -static void gtk_tree_model_sort_row_inserted (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data); -static void gtk_tree_model_sort_row_has_child_toggled (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data); -static void gtk_tree_model_sort_row_deleted (GtkTreeModel *model, - GtkTreePath *path, - gpointer data); -static void gtk_tree_model_sort_rows_reordered (GtkTreeModel *s_model, - GtkTreePath *s_path, - GtkTreeIter *s_iter, - int *new_order, - gpointer data); - -/* TreeModel interface */ -static GtkTreeModelFlags gtk_tree_model_sort_get_flags (GtkTreeModel *tree_model); -static int gtk_tree_model_sort_get_n_columns (GtkTreeModel *tree_model); -static GType gtk_tree_model_sort_get_column_type (GtkTreeModel *tree_model, - int index); -static gboolean gtk_tree_model_sort_get_iter (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreePath *path); -static GtkTreePath *gtk_tree_model_sort_get_path (GtkTreeModel *tree_model, - GtkTreeIter *iter); -static void gtk_tree_model_sort_get_value (GtkTreeModel *tree_model, - GtkTreeIter *iter, - int column, - GValue *value); -static gboolean gtk_tree_model_sort_iter_next (GtkTreeModel *tree_model, - GtkTreeIter *iter); -static gboolean gtk_tree_model_sort_iter_previous (GtkTreeModel *tree_model, - GtkTreeIter *iter); -static gboolean gtk_tree_model_sort_iter_children (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent); -static gboolean gtk_tree_model_sort_iter_has_child (GtkTreeModel *tree_model, - GtkTreeIter *iter); -static int gtk_tree_model_sort_iter_n_children (GtkTreeModel *tree_model, - GtkTreeIter *iter); -static gboolean gtk_tree_model_sort_iter_nth_child (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent, - int n); -static gboolean gtk_tree_model_sort_iter_parent (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *child); -static void gtk_tree_model_sort_ref_node (GtkTreeModel *tree_model, - GtkTreeIter *iter); -static void gtk_tree_model_sort_real_unref_node (GtkTreeModel *tree_model, - GtkTreeIter *iter, - gboolean propagate_unref); -static void gtk_tree_model_sort_unref_node (GtkTreeModel *tree_model, - GtkTreeIter *iter); - -/* TreeDragSource interface */ -static gboolean gtk_tree_model_sort_row_draggable (GtkTreeDragSource *drag_source, - GtkTreePath *path); -static GdkContentProvider * - gtk_tree_model_sort_drag_data_get (GtkTreeDragSource *drag_source, - GtkTreePath *path); -static gboolean gtk_tree_model_sort_drag_data_delete (GtkTreeDragSource *drag_source, - GtkTreePath *path); - -/* TreeSortable interface */ -static gboolean gtk_tree_model_sort_get_sort_column_id (GtkTreeSortable *sortable, - int *sort_column_id, - GtkSortType *order); -static void gtk_tree_model_sort_set_sort_column_id (GtkTreeSortable *sortable, - int sort_column_id, - GtkSortType order); -static void gtk_tree_model_sort_set_sort_func (GtkTreeSortable *sortable, - int sort_column_id, - GtkTreeIterCompareFunc func, - gpointer data, - GDestroyNotify destroy); -static void gtk_tree_model_sort_set_default_sort_func (GtkTreeSortable *sortable, - GtkTreeIterCompareFunc func, - gpointer data, - GDestroyNotify destroy); -static gboolean gtk_tree_model_sort_has_default_sort_func (GtkTreeSortable *sortable); - -/* Private functions (sort funcs, level handling and other utils) */ -static void gtk_tree_model_sort_build_level (GtkTreeModelSort *tree_model_sort, - SortLevel *parent_level, - SortElt *parent_elt); -static void gtk_tree_model_sort_free_level (GtkTreeModelSort *tree_model_sort, - SortLevel *sort_level, - gboolean unref); -static void gtk_tree_model_sort_increment_stamp (GtkTreeModelSort *tree_model_sort); -static void gtk_tree_model_sort_sort_level (GtkTreeModelSort *tree_model_sort, - SortLevel *level, - gboolean recurse, - gboolean emit_reordered); -static void gtk_tree_model_sort_sort (GtkTreeModelSort *tree_model_sort); -static gboolean gtk_tree_model_sort_insert_value (GtkTreeModelSort *tree_model_sort, - SortLevel *level, - GtkTreePath *s_path, - GtkTreeIter *s_iter); -static GtkTreePath *gtk_tree_model_sort_elt_get_path (SortLevel *level, - SortElt *elt); -static void gtk_tree_model_sort_set_model (GtkTreeModelSort *tree_model_sort, - GtkTreeModel *child_model); -static GtkTreePath *gtk_real_tree_model_sort_convert_child_path_to_path (GtkTreeModelSort *tree_model_sort, - GtkTreePath *child_path, - gboolean build_levels); - -static int gtk_tree_model_sort_compare_func (gconstpointer a, - gconstpointer b, - gpointer user_data); -static int gtk_tree_model_sort_offset_compare_func (gconstpointer a, - gconstpointer b, - gpointer user_data); -static void gtk_tree_model_sort_clear_cache_helper (GtkTreeModelSort *tree_model_sort, - SortLevel *level); - - -G_DEFINE_TYPE_WITH_CODE (GtkTreeModelSort, gtk_tree_model_sort, G_TYPE_OBJECT, - G_ADD_PRIVATE (GtkTreeModelSort) - G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, - gtk_tree_model_sort_tree_model_init) - G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_SORTABLE, - gtk_tree_model_sort_tree_sortable_init) - G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE, - gtk_tree_model_sort_drag_source_init)) - -static void -gtk_tree_model_sort_init (GtkTreeModelSort *tree_model_sort) -{ - GtkTreeModelSortPrivate *priv; - - tree_model_sort->priv = priv = - gtk_tree_model_sort_get_instance_private (tree_model_sort); - priv->sort_column_id = GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID; - priv->stamp = 0; - priv->zero_ref_count = 0; - priv->root = NULL; - priv->sort_list = NULL; -} - -static void -gtk_tree_model_sort_class_init (GtkTreeModelSortClass *class) -{ - GObjectClass *object_class; - - object_class = (GObjectClass *) class; - - object_class->set_property = gtk_tree_model_sort_set_property; - object_class->get_property = gtk_tree_model_sort_get_property; - - object_class->finalize = gtk_tree_model_sort_finalize; - - /* Properties */ - g_object_class_install_property (object_class, - PROP_MODEL, - g_param_spec_object ("model", NULL, NULL, - GTK_TYPE_TREE_MODEL, - GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); -} - -static void -gtk_tree_model_sort_tree_model_init (GtkTreeModelIface *iface) -{ - iface->get_flags = gtk_tree_model_sort_get_flags; - iface->get_n_columns = gtk_tree_model_sort_get_n_columns; - iface->get_column_type = gtk_tree_model_sort_get_column_type; - iface->get_iter = gtk_tree_model_sort_get_iter; - iface->get_path = gtk_tree_model_sort_get_path; - iface->get_value = gtk_tree_model_sort_get_value; - iface->iter_next = gtk_tree_model_sort_iter_next; - iface->iter_previous = gtk_tree_model_sort_iter_previous; - iface->iter_children = gtk_tree_model_sort_iter_children; - iface->iter_has_child = gtk_tree_model_sort_iter_has_child; - iface->iter_n_children = gtk_tree_model_sort_iter_n_children; - iface->iter_nth_child = gtk_tree_model_sort_iter_nth_child; - iface->iter_parent = gtk_tree_model_sort_iter_parent; - iface->ref_node = gtk_tree_model_sort_ref_node; - iface->unref_node = gtk_tree_model_sort_unref_node; -} - -static void -gtk_tree_model_sort_tree_sortable_init (GtkTreeSortableIface *iface) -{ - iface->get_sort_column_id = gtk_tree_model_sort_get_sort_column_id; - iface->set_sort_column_id = gtk_tree_model_sort_set_sort_column_id; - iface->set_sort_func = gtk_tree_model_sort_set_sort_func; - iface->set_default_sort_func = gtk_tree_model_sort_set_default_sort_func; - iface->has_default_sort_func = gtk_tree_model_sort_has_default_sort_func; -} - -static void -gtk_tree_model_sort_drag_source_init (GtkTreeDragSourceIface *iface) -{ - iface->row_draggable = gtk_tree_model_sort_row_draggable; - iface->drag_data_delete = gtk_tree_model_sort_drag_data_delete; - iface->drag_data_get = gtk_tree_model_sort_drag_data_get; -} - -/** - * gtk_tree_model_sort_new_with_model: (constructor) - * @child_model: A `GtkTreeModel` - * - * Creates a new `GtkTreeModelSort`, with @child_model as the child model. - * - * Returns: (transfer full) (type Gtk.TreeModelSort): A new `GtkTreeModelSort`. - */ -GtkTreeModel * -gtk_tree_model_sort_new_with_model (GtkTreeModel *child_model) -{ - GtkTreeModel *retval; - - g_return_val_if_fail (GTK_IS_TREE_MODEL (child_model), NULL); - - retval = g_object_new (gtk_tree_model_sort_get_type (), NULL); - - gtk_tree_model_sort_set_model (GTK_TREE_MODEL_SORT (retval), child_model); - - return retval; -} - -/* GObject callbacks */ -static void -gtk_tree_model_sort_finalize (GObject *object) -{ - GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) object; - GtkTreeModelSortPrivate *priv = tree_model_sort->priv; - - gtk_tree_model_sort_set_model (tree_model_sort, NULL); - - if (priv->root) - gtk_tree_model_sort_free_level (tree_model_sort, priv->root, TRUE); - - if (priv->sort_list) - { - _gtk_tree_data_list_header_free (priv->sort_list); - priv->sort_list = NULL; - } - - if (priv->default_sort_destroy) - { - priv->default_sort_destroy (priv->default_sort_data); - priv->default_sort_destroy = NULL; - priv->default_sort_data = NULL; - } - - - /* must chain up */ - G_OBJECT_CLASS (gtk_tree_model_sort_parent_class)->finalize (object); -} - -static void -gtk_tree_model_sort_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - GtkTreeModelSort *tree_model_sort = GTK_TREE_MODEL_SORT (object); - - switch (prop_id) - { - case PROP_MODEL: - gtk_tree_model_sort_set_model (tree_model_sort, g_value_get_object (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gtk_tree_model_sort_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GtkTreeModelSort *tree_model_sort = GTK_TREE_MODEL_SORT (object); - - switch (prop_id) - { - case PROP_MODEL: - g_value_set_object (value, gtk_tree_model_sort_get_model (tree_model_sort)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -/* helpers */ -static SortElt * -sort_elt_new (void) -{ - return g_slice_new (SortElt); -} - -static void -sort_elt_free (gpointer elt) -{ - g_slice_free (SortElt, elt); -} - -static void -increase_offset_iter (gpointer data, - gpointer user_data) -{ - SortElt *elt = data; - int offset = GPOINTER_TO_INT (user_data); - - if (elt->offset >= offset) - elt->offset++; -} - -static void -decrease_offset_iter (gpointer data, - gpointer user_data) -{ - SortElt *elt = data; - int offset = GPOINTER_TO_INT (user_data); - - if (elt->offset > offset) - elt->offset--; -} - -static void -fill_sort_data (SortData *data, - GtkTreeModelSort *tree_model_sort, - SortLevel *level) -{ - GtkTreeModelSortPrivate *priv = tree_model_sort->priv; - - data->tree_model_sort = tree_model_sort; - - if (priv->sort_column_id != GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID) - { - GtkTreeDataSortHeader *header; - - header = _gtk_tree_data_list_get_header (priv->sort_list, - priv->sort_column_id); - - g_return_if_fail (header != NULL); - g_return_if_fail (header->func != NULL); - - data->sort_func = header->func; - data->sort_data = header->data; - } - else - { - /* absolutely SHOULD NOT happen: */ - g_return_if_fail (priv->default_sort_func != NULL); - - data->sort_func = priv->default_sort_func; - data->sort_data = priv->default_sort_data; - } - - if (level->parent_elt) - { - data->parent_path = gtk_tree_model_sort_elt_get_path (level->parent_level, - level->parent_elt); - gtk_tree_path_append_index (data->parent_path, 0); - } - else - { - data->parent_path = gtk_tree_path_new_first (); - } - data->parent_path_depth = gtk_tree_path_get_depth (data->parent_path); - data->parent_path_indices = gtk_tree_path_get_indices (data->parent_path); -} - -static void -free_sort_data (SortData *data) -{ - gtk_tree_path_free (data->parent_path); -} - -static SortElt * -lookup_elt_with_offset (GtkTreeModelSort *tree_model_sort, - SortLevel *level, - int offset, - GSequenceIter **ret_siter) -{ - GSequenceIter *siter, *end_siter; - - /* FIXME: We have to do a search like this, because the sequence is not - * (always) sorted on offset order. Perhaps we should introduce a - * second sequence which is sorted on offset order. - */ - end_siter = g_sequence_get_end_iter (level->seq); - for (siter = g_sequence_get_begin_iter (level->seq); - siter != end_siter; - siter = g_sequence_iter_next (siter)) - { - SortElt *elt = g_sequence_get (siter); - - if (elt->offset == offset) - break; - } - - if (ret_siter) - *ret_siter = siter; - - return GET_ELT (siter); -} - - -static void -gtk_tree_model_sort_row_changed (GtkTreeModel *s_model, - GtkTreePath *start_s_path, - GtkTreeIter *start_s_iter, - gpointer data) -{ - GtkTreeModelSort *tree_model_sort = GTK_TREE_MODEL_SORT (data); - GtkTreeModelSortPrivate *priv = tree_model_sort->priv; - GtkTreePath *path = NULL; - GtkTreeIter iter; - GtkTreeIter tmpiter; - - SortElt *elt; - SortLevel *level; - SortData sort_data; - - gboolean free_s_path = FALSE; - - int index = 0, old_index; - - g_return_if_fail (start_s_path != NULL || start_s_iter != NULL); - - if (!start_s_path) - { - free_s_path = TRUE; - start_s_path = gtk_tree_model_get_path (s_model, start_s_iter); - } - - path = gtk_real_tree_model_sort_convert_child_path_to_path (tree_model_sort, - start_s_path, - FALSE); - if (!path) - { - if (free_s_path) - gtk_tree_path_free (start_s_path); - return; - } - - gtk_tree_model_get_iter (GTK_TREE_MODEL (data), &iter, path); - gtk_tree_model_sort_ref_node (GTK_TREE_MODEL (data), &iter); - - level = iter.user_data; - elt = iter.user_data2; - - if (g_sequence_get_length (level->seq) < 2 || - (priv->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID && - priv->default_sort_func == NO_SORT_FUNC)) - { - if (free_s_path) - gtk_tree_path_free (start_s_path); - - gtk_tree_model_row_changed (GTK_TREE_MODEL (data), path, &iter); - gtk_tree_model_sort_unref_node (GTK_TREE_MODEL (data), &iter); - - gtk_tree_path_free (path); - - return; - } - - if (GTK_TREE_MODEL_SORT_CACHE_CHILD_ITERS (tree_model_sort)) - tmpiter = elt->iter; - else - gtk_tree_model_get_iter (priv->child_model, - &tmpiter, start_s_path); - - old_index = g_sequence_iter_get_position (elt->siter); - - fill_sort_data (&sort_data, tree_model_sort, level); - g_sequence_sort_changed (elt->siter, - gtk_tree_model_sort_compare_func, - &sort_data); - free_sort_data (&sort_data); - - index = g_sequence_iter_get_position (elt->siter); - - /* Prepare the path for signal emission */ - gtk_tree_path_up (path); - gtk_tree_path_append_index (path, index); - - gtk_tree_model_sort_increment_stamp (tree_model_sort); - - /* if the item moved, then emit rows_reordered */ - if (old_index != index) - { - int *new_order; - int j; - - GtkTreePath *tmppath; - - new_order = g_new (int, g_sequence_get_length (level->seq)); - - for (j = 0; j < g_sequence_get_length (level->seq); j++) - { - if (index > old_index) - { - if (j == index) - new_order[j] = old_index; - else if (j >= old_index && j < index) - new_order[j] = j + 1; - else - new_order[j] = j; - } - else if (index < old_index) - { - if (j == index) - new_order[j] = old_index; - else if (j > index && j <= old_index) - new_order[j] = j - 1; - else - new_order[j] = j; - } - /* else? shouldn't really happen */ - } - - if (level->parent_elt) - { - iter.stamp = priv->stamp; - iter.user_data = level->parent_level; - iter.user_data2 = level->parent_elt; - - tmppath = gtk_tree_model_get_path (GTK_TREE_MODEL (tree_model_sort), &iter); - - gtk_tree_model_rows_reordered (GTK_TREE_MODEL (tree_model_sort), - tmppath, &iter, new_order); - } - else - { - /* toplevel */ - tmppath = gtk_tree_path_new (); - - gtk_tree_model_rows_reordered (GTK_TREE_MODEL (tree_model_sort), tmppath, - NULL, new_order); - } - - gtk_tree_path_free (tmppath); - g_free (new_order); - } - - /* emit row_changed signal (at new location) */ - gtk_tree_model_get_iter (GTK_TREE_MODEL (data), &iter, path); - gtk_tree_model_row_changed (GTK_TREE_MODEL (data), path, &iter); - gtk_tree_model_sort_unref_node (GTK_TREE_MODEL (data), &iter); - - gtk_tree_path_free (path); - if (free_s_path) - gtk_tree_path_free (start_s_path); -} - -static void -gtk_tree_model_sort_row_inserted (GtkTreeModel *s_model, - GtkTreePath *s_path, - GtkTreeIter *s_iter, - gpointer data) -{ - GtkTreeModelSort *tree_model_sort = GTK_TREE_MODEL_SORT (data); - GtkTreeModelSortPrivate *priv = tree_model_sort->priv; - GtkTreePath *path; - GtkTreeIter iter; - GtkTreeIter real_s_iter; - - int i = 0; - - gboolean free_s_path = FALSE; - - SortElt *elt; - SortLevel *level; - SortLevel *parent_level = NULL; - - parent_level = level = SORT_LEVEL (priv->root); - - g_return_if_fail (s_path != NULL || s_iter != NULL); - - if (!s_path) - { - s_path = gtk_tree_model_get_path (s_model, s_iter); - free_s_path = TRUE; - } - - if (!s_iter) - gtk_tree_model_get_iter (s_model, &real_s_iter, s_path); - else - real_s_iter = *s_iter; - - if (!priv->root) - { - gtk_tree_model_sort_build_level (tree_model_sort, NULL, NULL); - - /* the build level already put the inserted iter in the level, - so no need to handle this signal anymore */ - - goto done_and_submit; - } - - /* find the parent level */ - while (i < gtk_tree_path_get_depth (s_path) - 1) - { - if (!level) - { - /* level not yet build, we won't cover this signal */ - goto done; - } - - if (g_sequence_get_length (level->seq) < gtk_tree_path_get_indices (s_path)[i]) - { - g_warning ("%s: A node was inserted with a parent that's not in the tree.\n" - "This possibly means that a GtkTreeModel inserted a child node\n" - "before the parent was inserted.", - G_STRLOC); - goto done; - } - - elt = lookup_elt_with_offset (tree_model_sort, level, - gtk_tree_path_get_indices (s_path)[i], - NULL); - - g_return_if_fail (elt != NULL); - - if (!elt->children) - { - /* not covering this signal */ - goto done; - } - - level = elt->children; - parent_level = level; - i++; - } - - if (!parent_level) - goto done; - - if (level->ref_count == 0 && level != priv->root) - { - gtk_tree_model_sort_free_level (tree_model_sort, level, TRUE); - goto done; - } - - if (!gtk_tree_model_sort_insert_value (tree_model_sort, - parent_level, - s_path, - &real_s_iter)) - goto done; - - done_and_submit: - path = gtk_real_tree_model_sort_convert_child_path_to_path (tree_model_sort, - s_path, - FALSE); - - if (!path) - return; - - gtk_tree_model_sort_increment_stamp (tree_model_sort); - - gtk_tree_model_get_iter (GTK_TREE_MODEL (data), &iter, path); - gtk_tree_model_row_inserted (GTK_TREE_MODEL (data), path, &iter); - gtk_tree_path_free (path); - - done: - if (free_s_path) - gtk_tree_path_free (s_path); - - return; -} - -static void -gtk_tree_model_sort_row_has_child_toggled (GtkTreeModel *s_model, - GtkTreePath *s_path, - GtkTreeIter *s_iter, - gpointer data) -{ - GtkTreeModelSort *tree_model_sort = GTK_TREE_MODEL_SORT (data); - GtkTreePath *path; - GtkTreeIter iter; - - g_return_if_fail (s_path != NULL && s_iter != NULL); - - path = gtk_real_tree_model_sort_convert_child_path_to_path (tree_model_sort, s_path, FALSE); - if (path == NULL) - return; - - gtk_tree_model_get_iter (GTK_TREE_MODEL (data), &iter, path); - gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (data), path, &iter); - - gtk_tree_path_free (path); -} - -static void -gtk_tree_model_sort_row_deleted (GtkTreeModel *s_model, - GtkTreePath *s_path, - gpointer data) -{ - GtkTreeModelSort *tree_model_sort = GTK_TREE_MODEL_SORT (data); - GtkTreePath *path = NULL; - SortElt *elt; - SortLevel *level; - GtkTreeIter iter; - int offset; - - g_return_if_fail (s_path != NULL); - - path = gtk_real_tree_model_sort_convert_child_path_to_path (tree_model_sort, s_path, FALSE); - if (path == NULL) - return; - - gtk_tree_model_get_iter (GTK_TREE_MODEL (data), &iter, path); - - level = SORT_LEVEL (iter.user_data); - elt = SORT_ELT (iter.user_data2); - offset = elt->offset; - - gtk_tree_model_get_iter (GTK_TREE_MODEL (data), &iter, path); - - while (elt->ref_count > 0) - gtk_tree_model_sort_real_unref_node (GTK_TREE_MODEL (data), &iter, FALSE); - - /* If this node has children, we free the level (recursively) here - * and specify that unref may not be used, because parent and its - * children have been removed by now. - */ - if (elt->children) - gtk_tree_model_sort_free_level (tree_model_sort, - elt->children, FALSE); - - if (level->ref_count == 0 && g_sequence_get_length (level->seq) == 1) - { - gtk_tree_model_sort_increment_stamp (tree_model_sort); - gtk_tree_model_row_deleted (GTK_TREE_MODEL (data), path); - gtk_tree_path_free (path); - - if (level == tree_model_sort->priv->root) - { - gtk_tree_model_sort_free_level (tree_model_sort, - tree_model_sort->priv->root, - TRUE); - tree_model_sort->priv->root = NULL; - } - return; - } - - g_sequence_remove (elt->siter); - elt = NULL; - - /* The sequence is not ordered on offset, so we traverse the entire - * sequence. - */ - g_sequence_foreach (level->seq, decrease_offset_iter, - GINT_TO_POINTER (offset)); - - gtk_tree_model_sort_increment_stamp (tree_model_sort); - gtk_tree_model_row_deleted (GTK_TREE_MODEL (data), path); - - gtk_tree_path_free (path); -} - -static void -gtk_tree_model_sort_rows_reordered (GtkTreeModel *s_model, - GtkTreePath *s_path, - GtkTreeIter *s_iter, - int *new_order, - gpointer data) -{ - SortLevel *level; - GtkTreeIter iter; - GtkTreePath *path; - int *tmp_array; - int i, length; - GSequenceIter *siter, *end_siter; - GtkTreeModelSort *tree_model_sort = GTK_TREE_MODEL_SORT (data); - GtkTreeModelSortPrivate *priv = tree_model_sort->priv; - - g_return_if_fail (new_order != NULL); - - if (s_path == NULL || gtk_tree_path_get_depth (s_path) == 0) - { - if (priv->root == NULL) - return; - path = gtk_tree_path_new (); - level = SORT_LEVEL (priv->root); - } - else - { - SortElt *elt; - - path = gtk_real_tree_model_sort_convert_child_path_to_path (tree_model_sort, s_path, FALSE); - if (path == NULL) - return; - gtk_tree_model_get_iter (GTK_TREE_MODEL (data), &iter, path); - - elt = SORT_ELT (iter.user_data2); - - if (!elt->children) - { - gtk_tree_path_free (path); - return; - } - - level = elt->children; - } - - length = g_sequence_get_length (level->seq); - if (length < 2) - { - gtk_tree_path_free (path); - return; - } - - tmp_array = g_new (int, length); - - /* FIXME: I need to think about whether this can be done in a more - * efficient way? - */ - i = 0; - end_siter = g_sequence_get_end_iter (level->seq); - for (siter = g_sequence_get_begin_iter (level->seq); - siter != end_siter; - siter = g_sequence_iter_next (siter)) - { - int j; - SortElt *elt = g_sequence_get (siter); - - for (j = 0; j < length; j++) - { - if (elt->offset == new_order[j]) - tmp_array[i] = j; - } - - i++; - } - - /* This loop cannot be merged with the above loop nest, because that - * would introduce duplicate offsets. - */ - i = 0; - end_siter = g_sequence_get_end_iter (level->seq); - for (siter = g_sequence_get_begin_iter (level->seq); - siter != end_siter; - siter = g_sequence_iter_next (siter)) - { - SortElt *elt = g_sequence_get (siter); - - elt->offset = tmp_array[i]; - i++; - } - g_free (tmp_array); - - if (priv->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID && - priv->default_sort_func == NO_SORT_FUNC) - { - gtk_tree_model_sort_sort_level (tree_model_sort, level, - FALSE, FALSE); - gtk_tree_model_sort_increment_stamp (tree_model_sort); - - if (gtk_tree_path_get_depth (path)) - { - gtk_tree_model_get_iter (GTK_TREE_MODEL (tree_model_sort), - &iter, - path); - gtk_tree_model_rows_reordered (GTK_TREE_MODEL (tree_model_sort), - path, &iter, new_order); - } - else - { - gtk_tree_model_rows_reordered (GTK_TREE_MODEL (tree_model_sort), - path, NULL, new_order); - } - } - - gtk_tree_path_free (path); -} - -/* Fulfill our model requirements */ -static GtkTreeModelFlags -gtk_tree_model_sort_get_flags (GtkTreeModel *tree_model) -{ - GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model; - GtkTreeModelFlags flags; - - g_return_val_if_fail (tree_model_sort->priv->child_model != NULL, 0); - - flags = gtk_tree_model_get_flags (tree_model_sort->priv->child_model); - - if ((flags & GTK_TREE_MODEL_LIST_ONLY) == GTK_TREE_MODEL_LIST_ONLY) - return GTK_TREE_MODEL_LIST_ONLY; - - return 0; -} - -static int -gtk_tree_model_sort_get_n_columns (GtkTreeModel *tree_model) -{ - GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model; - - if (tree_model_sort->priv->child_model == 0) - return 0; - - return gtk_tree_model_get_n_columns (tree_model_sort->priv->child_model); -} - -static GType -gtk_tree_model_sort_get_column_type (GtkTreeModel *tree_model, - int index) -{ - GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model; - - g_return_val_if_fail (tree_model_sort->priv->child_model != NULL, G_TYPE_INVALID); - - return gtk_tree_model_get_column_type (tree_model_sort->priv->child_model, index); -} - -static gboolean -gtk_tree_model_sort_get_iter (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreePath *path) -{ - GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model; - GtkTreeModelSortPrivate *priv = tree_model_sort->priv; - int *indices; - SortElt *elt; - SortLevel *level; - int depth, i; - GSequenceIter *siter; - - g_return_val_if_fail (priv->child_model != NULL, FALSE); - - indices = gtk_tree_path_get_indices (path); - - if (priv->root == NULL) - gtk_tree_model_sort_build_level (tree_model_sort, NULL, NULL); - level = SORT_LEVEL (priv->root); - - depth = gtk_tree_path_get_depth (path); - if (depth == 0) - { - iter->stamp = 0; - return FALSE; - } - - for (i = 0; i < depth - 1; i++) - { - if ((level == NULL) || - (indices[i] >= g_sequence_get_length (level->seq))) - { - iter->stamp = 0; - return FALSE; - } - - siter = g_sequence_get_iter_at_pos (level->seq, indices[i]); - if (g_sequence_iter_is_end (siter)) - { - iter->stamp = 0; - return FALSE; - } - - elt = GET_ELT (siter); - g_assert (elt); - if (elt->children == NULL) - gtk_tree_model_sort_build_level (tree_model_sort, level, elt); - - level = elt->children; - } - - if (!level || indices[i] >= g_sequence_get_length (level->seq)) - { - iter->stamp = 0; - return FALSE; - } - - iter->stamp = priv->stamp; - iter->user_data = level; - - siter = g_sequence_get_iter_at_pos (level->seq, indices[depth - 1]); - if (g_sequence_iter_is_end (siter)) - { - iter->stamp = 0; - return FALSE; - } - iter->user_data2 = GET_ELT (siter); - - return TRUE; -} - -static GtkTreePath * -gtk_tree_model_sort_get_path (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model; - GtkTreeModelSortPrivate *priv = tree_model_sort->priv; - GtkTreePath *retval; - SortLevel *level; - SortElt *elt; - - g_return_val_if_fail (priv->child_model != NULL, NULL); - g_return_val_if_fail (priv->stamp == iter->stamp, NULL); - - retval = gtk_tree_path_new (); - - level = SORT_LEVEL (iter->user_data); - elt = SORT_ELT (iter->user_data2); - - while (level) - { - int index; - - index = g_sequence_iter_get_position (elt->siter); - gtk_tree_path_prepend_index (retval, index); - - elt = level->parent_elt; - level = level->parent_level; - } - - return retval; -} - -static void -gtk_tree_model_sort_get_value (GtkTreeModel *tree_model, - GtkTreeIter *iter, - int column, - GValue *value) -{ - GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model; - GtkTreeModelSortPrivate *priv = tree_model_sort->priv; - GtkTreeIter child_iter; - - g_return_if_fail (priv->child_model != NULL); - g_return_if_fail (VALID_ITER (iter, tree_model_sort)); - - GET_CHILD_ITER (tree_model_sort, &child_iter, iter); - gtk_tree_model_get_value (priv->child_model, - &child_iter, column, value); -} - -static gboolean -gtk_tree_model_sort_iter_next (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model; - GtkTreeModelSortPrivate *priv = tree_model_sort->priv; - SortElt *elt; - GSequenceIter *siter; - - g_return_val_if_fail (priv->child_model != NULL, FALSE); - g_return_val_if_fail (priv->stamp == iter->stamp, FALSE); - - elt = iter->user_data2; - - siter = g_sequence_iter_next (elt->siter); - if (g_sequence_iter_is_end (siter)) - { - iter->stamp = 0; - return FALSE; - } - iter->user_data2 = GET_ELT (siter); - - return TRUE; -} - -static gboolean -gtk_tree_model_sort_iter_previous (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model; - GtkTreeModelSortPrivate *priv = tree_model_sort->priv; - SortElt *elt; - GSequenceIter *siter; - - g_return_val_if_fail (priv->child_model != NULL, FALSE); - g_return_val_if_fail (priv->stamp == iter->stamp, FALSE); - - elt = iter->user_data2; - - if (g_sequence_iter_is_begin (elt->siter)) - { - iter->stamp = 0; - return FALSE; - } - - siter = g_sequence_iter_prev (elt->siter); - iter->user_data2 = GET_ELT (siter); - - return TRUE; -} - -static gboolean -gtk_tree_model_sort_iter_children (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent) -{ - GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model; - GtkTreeModelSortPrivate *priv = tree_model_sort->priv; - SortLevel *level; - - iter->stamp = 0; - g_return_val_if_fail (priv->child_model != NULL, FALSE); - if (parent) - g_return_val_if_fail (VALID_ITER (parent, tree_model_sort), FALSE); - - if (parent == NULL) - { - if (priv->root == NULL) - gtk_tree_model_sort_build_level (tree_model_sort, NULL, NULL); - if (priv->root == NULL) - return FALSE; - - level = priv->root; - iter->stamp = priv->stamp; - iter->user_data = level; - iter->user_data2 = g_sequence_get (g_sequence_get_begin_iter (level->seq)); - } - else - { - SortElt *elt; - - level = SORT_LEVEL (parent->user_data); - elt = SORT_ELT (parent->user_data2); - - if (elt->children == NULL) - gtk_tree_model_sort_build_level (tree_model_sort, level, elt); - - if (elt->children == NULL) - return FALSE; - - iter->stamp = priv->stamp; - iter->user_data = elt->children; - iter->user_data2 = g_sequence_get (g_sequence_get_begin_iter (elt->children->seq)); - } - - return TRUE; -} - -static gboolean -gtk_tree_model_sort_iter_has_child (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model; - GtkTreeModelSortPrivate *priv = tree_model_sort->priv; - GtkTreeIter child_iter; - - g_return_val_if_fail (priv->child_model != NULL, FALSE); - g_return_val_if_fail (VALID_ITER (iter, tree_model_sort), FALSE); - - GET_CHILD_ITER (tree_model_sort, &child_iter, iter); - - return gtk_tree_model_iter_has_child (priv->child_model, &child_iter); -} - -static int -gtk_tree_model_sort_iter_n_children (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model; - GtkTreeModelSortPrivate *priv = tree_model_sort->priv; - GtkTreeIter child_iter; - - g_return_val_if_fail (priv->child_model != NULL, 0); - if (iter) - g_return_val_if_fail (VALID_ITER (iter, tree_model_sort), 0); - - if (iter == NULL) - return gtk_tree_model_iter_n_children (priv->child_model, NULL); - - GET_CHILD_ITER (tree_model_sort, &child_iter, iter); - - return gtk_tree_model_iter_n_children (priv->child_model, &child_iter); -} - -static gboolean -gtk_tree_model_sort_iter_nth_child (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent, - int n) -{ - GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model; - SortLevel *level; - /* We have this for the iter == parent case */ - GtkTreeIter children; - - if (parent) - g_return_val_if_fail (VALID_ITER (parent, tree_model_sort), FALSE); - - /* Use this instead of has_child to force us to build the level, if needed */ - if (gtk_tree_model_sort_iter_children (tree_model, &children, parent) == FALSE) - { - iter->stamp = 0; - return FALSE; - } - - level = children.user_data; - if (n >= g_sequence_get_length (level->seq)) - { - iter->stamp = 0; - return FALSE; - } - - iter->stamp = tree_model_sort->priv->stamp; - iter->user_data = level; - iter->user_data2 = g_sequence_get (g_sequence_get_iter_at_pos (level->seq, n)); - - return TRUE; -} - -static gboolean -gtk_tree_model_sort_iter_parent (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *child) -{ - GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model; - GtkTreeModelSortPrivate *priv = tree_model_sort->priv; - SortLevel *level; - - iter->stamp = 0; - g_return_val_if_fail (priv->child_model != NULL, FALSE); - g_return_val_if_fail (VALID_ITER (child, tree_model_sort), FALSE); - - level = child->user_data; - - if (level->parent_level) - { - iter->stamp = priv->stamp; - iter->user_data = level->parent_level; - iter->user_data2 = level->parent_elt; - - return TRUE; - } - return FALSE; -} - -static void -gtk_tree_model_sort_ref_node (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model; - GtkTreeModelSortPrivate *priv = tree_model_sort->priv; - GtkTreeIter child_iter; - SortLevel *level; - SortElt *elt; - - g_return_if_fail (priv->child_model != NULL); - g_return_if_fail (VALID_ITER (iter, tree_model_sort)); - - GET_CHILD_ITER (tree_model_sort, &child_iter, iter); - - /* Reference the node in the child model */ - gtk_tree_model_ref_node (priv->child_model, &child_iter); - - /* Increase the reference count of this element and its level */ - level = iter->user_data; - elt = iter->user_data2; - - elt->ref_count++; - level->ref_count++; - - if (level->ref_count == 1) - { - SortLevel *parent_level = level->parent_level; - SortElt *parent_elt = level->parent_elt; - - /* We were at zero -- time to decrement the zero_ref_count val */ - while (parent_level) - { - parent_elt->zero_ref_count--; - - parent_elt = parent_level->parent_elt; - parent_level = parent_level->parent_level; - } - - if (priv->root != level) - priv->zero_ref_count--; - } -} - -static void -gtk_tree_model_sort_real_unref_node (GtkTreeModel *tree_model, - GtkTreeIter *iter, - gboolean propagate_unref) -{ - GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model; - GtkTreeModelSortPrivate *priv = tree_model_sort->priv; - SortLevel *level; - SortElt *elt; - - g_return_if_fail (priv->child_model != NULL); - g_return_if_fail (VALID_ITER (iter, tree_model_sort)); - - if (propagate_unref) - { - GtkTreeIter child_iter; - - GET_CHILD_ITER (tree_model_sort, &child_iter, iter); - gtk_tree_model_unref_node (priv->child_model, &child_iter); - } - - level = iter->user_data; - elt = iter->user_data2; - - g_return_if_fail (elt->ref_count > 0); - - elt->ref_count--; - level->ref_count--; - - if (level->ref_count == 0) - { - SortLevel *parent_level = level->parent_level; - SortElt *parent_elt = level->parent_elt; - - /* We are at zero -- time to increment the zero_ref_count val */ - while (parent_level) - { - parent_elt->zero_ref_count++; - - parent_elt = parent_level->parent_elt; - parent_level = parent_level->parent_level; - } - - if (priv->root != level) - priv->zero_ref_count++; - } -} - -static void -gtk_tree_model_sort_unref_node (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - gtk_tree_model_sort_real_unref_node (tree_model, iter, TRUE); -} - -/* Sortable interface */ -static gboolean -gtk_tree_model_sort_get_sort_column_id (GtkTreeSortable *sortable, - int *sort_column_id, - GtkSortType *order) -{ - GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *)sortable; - GtkTreeModelSortPrivate *priv = tree_model_sort->priv; - - if (sort_column_id) - *sort_column_id = priv->sort_column_id; - if (order) - *order = priv->order; - - if (priv->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID || - priv->sort_column_id == GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID) - return FALSE; - - return TRUE; -} - -static void -gtk_tree_model_sort_set_sort_column_id (GtkTreeSortable *sortable, - int sort_column_id, - GtkSortType order) -{ - GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *)sortable; - GtkTreeModelSortPrivate *priv = tree_model_sort->priv; - - if (priv->sort_column_id == sort_column_id && priv->order == order) - return; - - if (sort_column_id != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID) - { - if (sort_column_id != GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID) - { - GtkTreeDataSortHeader *header = NULL; - - header = _gtk_tree_data_list_get_header (priv->sort_list, - sort_column_id); - - /* we want to make sure that we have a function */ - g_return_if_fail (header != NULL); - g_return_if_fail (header->func != NULL); - } - else - g_return_if_fail (priv->default_sort_func != NULL); - } - - priv->sort_column_id = sort_column_id; - priv->order = order; - - gtk_tree_sortable_sort_column_changed (sortable); - - gtk_tree_model_sort_sort (tree_model_sort); -} - -static void -gtk_tree_model_sort_set_sort_func (GtkTreeSortable *sortable, - int sort_column_id, - GtkTreeIterCompareFunc func, - gpointer data, - GDestroyNotify destroy) -{ - GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) sortable; - GtkTreeModelSortPrivate *priv = tree_model_sort->priv; - - priv->sort_list = _gtk_tree_data_list_set_header (priv->sort_list, - sort_column_id, - func, data, destroy); - - if (priv->sort_column_id == sort_column_id) - gtk_tree_model_sort_sort (tree_model_sort); -} - -static void -gtk_tree_model_sort_set_default_sort_func (GtkTreeSortable *sortable, - GtkTreeIterCompareFunc func, - gpointer data, - GDestroyNotify destroy) -{ - GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *)sortable; - GtkTreeModelSortPrivate *priv = tree_model_sort->priv; - - if (priv->default_sort_destroy) - { - GDestroyNotify d = priv->default_sort_destroy; - - priv->default_sort_destroy = NULL; - d (priv->default_sort_data); - } - - priv->default_sort_func = func; - priv->default_sort_data = data; - priv->default_sort_destroy = destroy; - - if (priv->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID) - gtk_tree_model_sort_sort (tree_model_sort); -} - -static gboolean -gtk_tree_model_sort_has_default_sort_func (GtkTreeSortable *sortable) -{ - GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *)sortable; - - return (tree_model_sort->priv->default_sort_func != NO_SORT_FUNC); -} - -/* DragSource interface */ -static gboolean -gtk_tree_model_sort_row_draggable (GtkTreeDragSource *drag_source, - GtkTreePath *path) -{ - GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *)drag_source; - GtkTreePath *child_path; - gboolean draggable; - - child_path = gtk_tree_model_sort_convert_path_to_child_path (tree_model_sort, - path); - draggable = gtk_tree_drag_source_row_draggable (GTK_TREE_DRAG_SOURCE (tree_model_sort->priv->child_model), child_path); - gtk_tree_path_free (child_path); - - return draggable; -} - -static GdkContentProvider * -gtk_tree_model_sort_drag_data_get (GtkTreeDragSource *drag_source, - GtkTreePath *path) -{ - GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *)drag_source; - GtkTreePath *child_path; - GdkContentProvider *gotten; - - child_path = gtk_tree_model_sort_convert_path_to_child_path (tree_model_sort, path); - gotten = gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (tree_model_sort->priv->child_model), child_path); - gtk_tree_path_free (child_path); - - return gotten; -} - -static gboolean -gtk_tree_model_sort_drag_data_delete (GtkTreeDragSource *drag_source, - GtkTreePath *path) -{ - GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *)drag_source; - GtkTreePath *child_path; - gboolean deleted; - - child_path = gtk_tree_model_sort_convert_path_to_child_path (tree_model_sort, path); - deleted = gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (tree_model_sort->priv->child_model), child_path); - gtk_tree_path_free (child_path); - - return deleted; -} - -/* sorting code - private */ -static int -gtk_tree_model_sort_compare_func (gconstpointer a, - gconstpointer b, - gpointer user_data) -{ - SortData *data = (SortData *)user_data; - GtkTreeModelSort *tree_model_sort = data->tree_model_sort; - GtkTreeModelSortPrivate *priv = tree_model_sort->priv; - const SortElt *sa = a; - const SortElt *sb = b; - - GtkTreeIter iter_a, iter_b; - int retval; - - if (GTK_TREE_MODEL_SORT_CACHE_CHILD_ITERS (tree_model_sort)) - { - iter_a = sa->iter; - iter_b = sb->iter; - } - else - { - data->parent_path_indices [data->parent_path_depth-1] = sa->offset; - gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->child_model), &iter_a, data->parent_path); - data->parent_path_indices [data->parent_path_depth-1] = sb->offset; - gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->child_model), &iter_b, data->parent_path); - } - - retval = (* data->sort_func) (GTK_TREE_MODEL (priv->child_model), - &iter_a, &iter_b, - data->sort_data); - - if (priv->order == GTK_SORT_DESCENDING) - { - if (retval > 0) - retval = -1; - else if (retval < 0) - retval = 1; - } - - return retval; -} - -static int -gtk_tree_model_sort_offset_compare_func (gconstpointer a, - gconstpointer b, - gpointer user_data) -{ - int retval; - - const SortElt *sa = (SortElt *)a; - const SortElt *sb = (SortElt *)b; - - SortData *data = (SortData *)user_data; - - if (sa->offset < sb->offset) - retval = -1; - else if (sa->offset > sb->offset) - retval = 1; - else - retval = 0; - - if (data->tree_model_sort->priv->order == GTK_SORT_DESCENDING) - { - if (retval > 0) - retval = -1; - else if (retval < 0) - retval = 1; - } - - return retval; -} - -static void -gtk_tree_model_sort_sort_level (GtkTreeModelSort *tree_model_sort, - SortLevel *level, - gboolean recurse, - gboolean emit_reordered) -{ - GtkTreeModelSortPrivate *priv = tree_model_sort->priv; - int i; - GSequenceIter *begin_siter, *end_siter, *siter; - SortElt *begin_elt; - int *new_order; - - GtkTreeIter iter; - GtkTreePath *path; - - SortData data; - - g_return_if_fail (level != NULL); - - begin_siter = g_sequence_get_begin_iter (level->seq); - begin_elt = g_sequence_get (begin_siter); - - if (g_sequence_get_length (level->seq) < 1 && !begin_elt->children) - return; - - iter.stamp = priv->stamp; - iter.user_data = level; - iter.user_data2 = begin_elt; - - gtk_tree_model_sort_ref_node (GTK_TREE_MODEL (tree_model_sort), &iter); - - i = 0; - end_siter = g_sequence_get_end_iter (level->seq); - for (siter = g_sequence_get_begin_iter (level->seq); - siter != end_siter; - siter = g_sequence_iter_next (siter)) - { - SortElt *elt = g_sequence_get (siter); - - elt->old_index = i; - i++; - } - - fill_sort_data (&data, tree_model_sort, level); - - if (data.sort_func == NO_SORT_FUNC) - g_sequence_sort (level->seq, gtk_tree_model_sort_offset_compare_func, - &data); - else - g_sequence_sort (level->seq, gtk_tree_model_sort_compare_func, &data); - - free_sort_data (&data); - - new_order = g_new (int, g_sequence_get_length (level->seq)); - - i = 0; - end_siter = g_sequence_get_end_iter (level->seq); - for (siter = g_sequence_get_begin_iter (level->seq); - siter != end_siter; - siter = g_sequence_iter_next (siter)) - { - SortElt *elt = g_sequence_get (siter); - - new_order[i++] = elt->old_index; - } - - if (emit_reordered) - { - gtk_tree_model_sort_increment_stamp (tree_model_sort); - if (level->parent_elt) - { - iter.stamp = priv->stamp; - iter.user_data = level->parent_level; - iter.user_data2 = level->parent_elt; - - path = gtk_tree_model_get_path (GTK_TREE_MODEL (tree_model_sort), - &iter); - - gtk_tree_model_rows_reordered (GTK_TREE_MODEL (tree_model_sort), path, - &iter, new_order); - } - else - { - /* toplevel list */ - path = gtk_tree_path_new (); - gtk_tree_model_rows_reordered (GTK_TREE_MODEL (tree_model_sort), path, - NULL, new_order); - } - - gtk_tree_path_free (path); - } - - /* recurse, if possible */ - if (recurse) - { - end_siter = g_sequence_get_end_iter (level->seq); - for (siter = g_sequence_get_begin_iter (level->seq); - siter != end_siter; - siter = g_sequence_iter_next (siter)) - { - SortElt *elt = g_sequence_get (siter); - - if (elt->children) - gtk_tree_model_sort_sort_level (tree_model_sort, - elt->children, - TRUE, emit_reordered); - } - } - - g_free (new_order); - - /* get the iter we referenced at the beginning of this function and - * unref it again - */ - iter.stamp = priv->stamp; - iter.user_data = level; - iter.user_data2 = begin_elt; - - gtk_tree_model_sort_unref_node (GTK_TREE_MODEL (tree_model_sort), &iter); -} - -static void -gtk_tree_model_sort_sort (GtkTreeModelSort *tree_model_sort) -{ - GtkTreeModelSortPrivate *priv = tree_model_sort->priv; - - if (priv->sort_column_id == GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID) - return; - - if (!priv->root) - return; - - if (priv->sort_column_id != GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID) - { - GtkTreeDataSortHeader *header = NULL; - - header = _gtk_tree_data_list_get_header (priv->sort_list, - priv->sort_column_id); - - /* we want to make sure that we have a function */ - g_return_if_fail (header != NULL); - g_return_if_fail (header->func != NULL); - } - else - g_return_if_fail (priv->default_sort_func != NULL); - - gtk_tree_model_sort_sort_level (tree_model_sort, priv->root, - TRUE, TRUE); -} - -/* signal helpers */ -static gboolean -gtk_tree_model_sort_insert_value (GtkTreeModelSort *tree_model_sort, - SortLevel *level, - GtkTreePath *s_path, - GtkTreeIter *s_iter) -{ - GtkTreeModelSortPrivate *priv = tree_model_sort->priv; - SortElt *elt; - SortData data; - int offset; - - elt = sort_elt_new (); - - offset = gtk_tree_path_get_indices (s_path)[gtk_tree_path_get_depth (s_path) - 1]; - - if (GTK_TREE_MODEL_SORT_CACHE_CHILD_ITERS (tree_model_sort)) - elt->iter = *s_iter; - elt->offset = offset; - elt->zero_ref_count = 0; - elt->ref_count = 0; - elt->children = NULL; - - /* update all larger offsets */ - g_sequence_foreach (level->seq, increase_offset_iter, GINT_TO_POINTER (offset)); - - fill_sort_data (&data, tree_model_sort, level); - - if (priv->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID && - priv->default_sort_func == NO_SORT_FUNC) - { - elt->siter = g_sequence_insert_sorted (level->seq, elt, - gtk_tree_model_sort_offset_compare_func, - &data); - } - else - { - elt->siter = g_sequence_insert_sorted (level->seq, elt, - gtk_tree_model_sort_compare_func, - &data); - } - - free_sort_data (&data); - - return TRUE; -} - -/* sort elt stuff */ -static GtkTreePath * -gtk_tree_model_sort_elt_get_path (SortLevel *level, - SortElt *elt) -{ - SortLevel *walker = level; - SortElt *walker2 = elt; - GtkTreePath *path; - - g_return_val_if_fail (level != NULL, NULL); - g_return_val_if_fail (elt != NULL, NULL); - - path = gtk_tree_path_new (); - - while (walker) - { - gtk_tree_path_prepend_index (path, walker2->offset); - - if (!walker->parent_level) - break; - - walker2 = walker->parent_elt; - walker = walker->parent_level; - } - - return path; -} - -/** - * gtk_tree_model_sort_set_model: - * @tree_model_sort: The `GtkTreeModelSort`. - * @child_model: (nullable): A `GtkTreeModel` - * - * Sets the model of @tree_model_sort to be @model. If @model is %NULL, - * then the old model is unset. The sort function is unset as a result - * of this call. The model will be in an unsorted state until a sort - * function is set. - **/ -static void -gtk_tree_model_sort_set_model (GtkTreeModelSort *tree_model_sort, - GtkTreeModel *child_model) -{ - GtkTreeModelSortPrivate *priv = tree_model_sort->priv; - - if (child_model) - g_object_ref (child_model); - - if (priv->child_model) - { - g_signal_handler_disconnect (priv->child_model, - priv->changed_id); - g_signal_handler_disconnect (priv->child_model, - priv->inserted_id); - g_signal_handler_disconnect (priv->child_model, - priv->has_child_toggled_id); - g_signal_handler_disconnect (priv->child_model, - priv->deleted_id); - g_signal_handler_disconnect (priv->child_model, - priv->reordered_id); - - /* reset our state */ - if (priv->root) - gtk_tree_model_sort_free_level (tree_model_sort, priv->root, TRUE); - priv->root = NULL; - _gtk_tree_data_list_header_free (priv->sort_list); - priv->sort_list = NULL; - g_object_unref (priv->child_model); - } - - priv->child_model = child_model; - - if (child_model) - { - GType *types; - int i, n_columns; - - priv->changed_id = - g_signal_connect (child_model, "row-changed", - G_CALLBACK (gtk_tree_model_sort_row_changed), - tree_model_sort); - priv->inserted_id = - g_signal_connect (child_model, "row-inserted", - G_CALLBACK (gtk_tree_model_sort_row_inserted), - tree_model_sort); - priv->has_child_toggled_id = - g_signal_connect (child_model, "row-has-child-toggled", - G_CALLBACK (gtk_tree_model_sort_row_has_child_toggled), - tree_model_sort); - priv->deleted_id = - g_signal_connect (child_model, "row-deleted", - G_CALLBACK (gtk_tree_model_sort_row_deleted), - tree_model_sort); - priv->reordered_id = - g_signal_connect (child_model, "rows-reordered", - G_CALLBACK (gtk_tree_model_sort_rows_reordered), - tree_model_sort); - - priv->child_flags = gtk_tree_model_get_flags (child_model); - n_columns = gtk_tree_model_get_n_columns (child_model); - - types = g_new (GType, n_columns); - for (i = 0; i < n_columns; i++) - types[i] = gtk_tree_model_get_column_type (child_model, i); - - priv->sort_list = _gtk_tree_data_list_header_new (n_columns, types); - g_free (types); - - priv->default_sort_func = NO_SORT_FUNC; - priv->stamp = g_random_int (); - } -} - -/** - * gtk_tree_model_sort_get_model: - * @tree_model: a `GtkTreeModelSort` - * - * Returns the model the `GtkTreeModelSort` is sorting. - * - * Returns: (transfer none): the "child model" being sorted - **/ -GtkTreeModel * -gtk_tree_model_sort_get_model (GtkTreeModelSort *tree_model) -{ - g_return_val_if_fail (GTK_IS_TREE_MODEL_SORT (tree_model), NULL); - - return tree_model->priv->child_model; -} - - -static GtkTreePath * -gtk_real_tree_model_sort_convert_child_path_to_path (GtkTreeModelSort *tree_model_sort, - GtkTreePath *child_path, - gboolean build_levels) -{ - GtkTreeModelSortPrivate *priv = tree_model_sort->priv; - int *child_indices; - GtkTreePath *retval; - SortLevel *level; - int i; - - g_return_val_if_fail (priv->child_model != NULL, NULL); - g_return_val_if_fail (child_path != NULL, NULL); - - retval = gtk_tree_path_new (); - child_indices = gtk_tree_path_get_indices (child_path); - - if (priv->root == NULL && build_levels) - gtk_tree_model_sort_build_level (tree_model_sort, NULL, NULL); - level = SORT_LEVEL (priv->root); - - for (i = 0; i < gtk_tree_path_get_depth (child_path); i++) - { - SortElt *tmp; - GSequenceIter *siter; - gboolean found_child = FALSE; - - if (!level) - { - gtk_tree_path_free (retval); - return NULL; - } - - if (child_indices[i] >= g_sequence_get_length (level->seq)) - { - gtk_tree_path_free (retval); - return NULL; - } - tmp = lookup_elt_with_offset (tree_model_sort, level, - child_indices[i], &siter); - if (tmp) - { - gtk_tree_path_append_index (retval, g_sequence_iter_get_position (siter)); - if (tmp->children == NULL && build_levels) - gtk_tree_model_sort_build_level (tree_model_sort, level, tmp); - - level = tmp->children; - found_child = TRUE; - } - - if (! found_child) - { - gtk_tree_path_free (retval); - return NULL; - } - } - - return retval; -} - - -/** - * gtk_tree_model_sort_convert_child_path_to_path: - * @tree_model_sort: A `GtkTreeModelSort` - * @child_path: A `GtkTreePath` to convert - * - * Converts @child_path to a path relative to @tree_model_sort. That is, - * @child_path points to a path in the child model. The returned path will - * point to the same row in the sorted model. If @child_path isn’t a valid - * path on the child model, then %NULL is returned. - * - * Returns: (nullable) (transfer full): A newly allocated `GtkTreePath` - **/ -GtkTreePath * -gtk_tree_model_sort_convert_child_path_to_path (GtkTreeModelSort *tree_model_sort, - GtkTreePath *child_path) -{ - g_return_val_if_fail (GTK_IS_TREE_MODEL_SORT (tree_model_sort), NULL); - g_return_val_if_fail (tree_model_sort->priv->child_model != NULL, NULL); - g_return_val_if_fail (child_path != NULL, NULL); - - return gtk_real_tree_model_sort_convert_child_path_to_path (tree_model_sort, child_path, TRUE); -} - -/** - * gtk_tree_model_sort_convert_child_iter_to_iter: - * @tree_model_sort: A `GtkTreeModelSort` - * @sort_iter: (out): An uninitialized `GtkTreeIter` - * @child_iter: A valid `GtkTreeIter` pointing to a row on the child model - * - * Sets @sort_iter to point to the row in @tree_model_sort that corresponds to - * the row pointed at by @child_iter. If @sort_iter was not set, %FALSE - * is returned. Note: a boolean is only returned since 2.14. - * - * Returns: %TRUE, if @sort_iter was set, i.e. if @sort_iter is a - * valid iterator pointer to a visible row in the child model. - **/ -gboolean -gtk_tree_model_sort_convert_child_iter_to_iter (GtkTreeModelSort *tree_model_sort, - GtkTreeIter *sort_iter, - GtkTreeIter *child_iter) -{ - gboolean ret; - GtkTreePath *child_path, *path; - GtkTreeModelSortPrivate *priv = tree_model_sort->priv; - - g_return_val_if_fail (GTK_IS_TREE_MODEL_SORT (tree_model_sort), FALSE); - g_return_val_if_fail (priv->child_model != NULL, FALSE); - g_return_val_if_fail (sort_iter != NULL, FALSE); - g_return_val_if_fail (child_iter != NULL, FALSE); - g_return_val_if_fail (sort_iter != child_iter, FALSE); - - sort_iter->stamp = 0; - - child_path = gtk_tree_model_get_path (priv->child_model, child_iter); - g_return_val_if_fail (child_path != NULL, FALSE); - - path = gtk_tree_model_sort_convert_child_path_to_path (tree_model_sort, child_path); - gtk_tree_path_free (child_path); - - if (!path) - { - g_warning ("%s: The conversion of the child path to a GtkTreeModel sort path failed", G_STRLOC); - return FALSE; - } - - ret = gtk_tree_model_get_iter (GTK_TREE_MODEL (tree_model_sort), - sort_iter, path); - gtk_tree_path_free (path); - - return ret; -} - -/** - * gtk_tree_model_sort_convert_path_to_child_path: - * @tree_model_sort: A `GtkTreeModelSort` - * @sorted_path: A `GtkTreePath` to convert - * - * Converts @sorted_path to a path on the child model of @tree_model_sort. - * That is, @sorted_path points to a location in @tree_model_sort. The - * returned path will point to the same location in the model not being - * sorted. If @sorted_path does not point to a location in the child model, - * %NULL is returned. - * - * Returns: (nullable) (transfer full): A newly allocated `GtkTreePath` - **/ -GtkTreePath * -gtk_tree_model_sort_convert_path_to_child_path (GtkTreeModelSort *tree_model_sort, - GtkTreePath *sorted_path) -{ - GtkTreeModelSortPrivate *priv = tree_model_sort->priv; - int *sorted_indices; - GtkTreePath *retval; - SortLevel *level; - int i; - - g_return_val_if_fail (GTK_IS_TREE_MODEL_SORT (tree_model_sort), NULL); - g_return_val_if_fail (priv->child_model != NULL, NULL); - g_return_val_if_fail (sorted_path != NULL, NULL); - - retval = gtk_tree_path_new (); - sorted_indices = gtk_tree_path_get_indices (sorted_path); - if (priv->root == NULL) - gtk_tree_model_sort_build_level (tree_model_sort, NULL, NULL); - level = SORT_LEVEL (priv->root); - - for (i = 0; i < gtk_tree_path_get_depth (sorted_path); i++) - { - SortElt *elt = NULL; - GSequenceIter *siter; - - if ((level == NULL) || - (g_sequence_get_length (level->seq) <= sorted_indices[i])) - { - gtk_tree_path_free (retval); - return NULL; - } - - siter = g_sequence_get_iter_at_pos (level->seq, sorted_indices[i]); - if (g_sequence_iter_is_end (siter)) - { - gtk_tree_path_free (retval); - return NULL; - } - - elt = GET_ELT (siter); - g_assert (elt); - if (elt->children == NULL) - gtk_tree_model_sort_build_level (tree_model_sort, level, elt); - - if (level == NULL) - { - gtk_tree_path_free (retval); - break; - } - - gtk_tree_path_append_index (retval, elt->offset); - level = elt->children; - } - - return retval; -} - -/** - * gtk_tree_model_sort_convert_iter_to_child_iter: - * @tree_model_sort: A `GtkTreeModelSort` - * @child_iter: (out): An uninitialized `GtkTreeIter` - * @sorted_iter: A valid `GtkTreeIter` pointing to a row on @tree_model_sort. - * - * Sets @child_iter to point to the row pointed to by @sorted_iter. - **/ -void -gtk_tree_model_sort_convert_iter_to_child_iter (GtkTreeModelSort *tree_model_sort, - GtkTreeIter *child_iter, - GtkTreeIter *sorted_iter) -{ - GtkTreeModelSortPrivate *priv = tree_model_sort->priv; - g_return_if_fail (GTK_IS_TREE_MODEL_SORT (tree_model_sort)); - g_return_if_fail (priv->child_model != NULL); - g_return_if_fail (child_iter != NULL); - g_return_if_fail (VALID_ITER (sorted_iter, tree_model_sort)); - g_return_if_fail (sorted_iter != child_iter); - - if (GTK_TREE_MODEL_SORT_CACHE_CHILD_ITERS (tree_model_sort)) - { - *child_iter = SORT_ELT (sorted_iter->user_data2)->iter; - } - else - { - GtkTreePath *path; - gboolean valid = FALSE; - - path = gtk_tree_model_sort_elt_get_path (sorted_iter->user_data, - sorted_iter->user_data2); - valid = gtk_tree_model_get_iter (priv->child_model, child_iter, path); - gtk_tree_path_free (path); - - g_return_if_fail (valid == TRUE); - } -} - -static void -gtk_tree_model_sort_build_level (GtkTreeModelSort *tree_model_sort, - SortLevel *parent_level, - SortElt *parent_elt) -{ - GtkTreeModelSortPrivate *priv = tree_model_sort->priv; - GtkTreeIter iter; - SortLevel *new_level; - int length = 0; - int i; - - g_assert (priv->child_model != NULL); - - if (parent_level == NULL) - { - if (gtk_tree_model_get_iter_first (priv->child_model, &iter) == FALSE) - return; - length = gtk_tree_model_iter_n_children (priv->child_model, NULL); - } - else - { - GtkTreeIter parent_iter; - GtkTreeIter child_parent_iter; - - parent_iter.stamp = priv->stamp; - parent_iter.user_data = parent_level; - parent_iter.user_data2 = parent_elt; - - gtk_tree_model_sort_convert_iter_to_child_iter (tree_model_sort, - &child_parent_iter, - &parent_iter); - if (gtk_tree_model_iter_children (priv->child_model, - &iter, - &child_parent_iter) == FALSE) - return; - - /* stamp may have changed */ - gtk_tree_model_sort_convert_iter_to_child_iter (tree_model_sort, - &child_parent_iter, - &parent_iter); - - length = gtk_tree_model_iter_n_children (priv->child_model, &child_parent_iter); - - gtk_tree_model_sort_ref_node (GTK_TREE_MODEL (tree_model_sort), - &parent_iter); - } - - g_return_if_fail (length > 0); - - new_level = g_new (SortLevel, 1); - new_level->seq = g_sequence_new (sort_elt_free); - new_level->ref_count = 0; - new_level->parent_level = parent_level; - new_level->parent_elt = parent_elt; - - if (parent_elt) - parent_elt->children = new_level; - else - priv->root = new_level; - - /* increase the count of zero ref_counts.*/ - while (parent_level) - { - parent_elt->zero_ref_count++; - - parent_elt = parent_level->parent_elt; - parent_level = parent_level->parent_level; - } - - if (new_level != priv->root) - priv->zero_ref_count++; - - for (i = 0; i < length; i++) - { - SortElt *sort_elt; - - sort_elt = sort_elt_new (); - sort_elt->offset = i; - sort_elt->zero_ref_count = 0; - sort_elt->ref_count = 0; - sort_elt->children = NULL; - - if (GTK_TREE_MODEL_SORT_CACHE_CHILD_ITERS (tree_model_sort)) - { - sort_elt->iter = iter; - if (gtk_tree_model_iter_next (priv->child_model, &iter) == FALSE && - i < length - 1) - { - if (parent_level) - { - GtkTreePath *level; - char *str; - - level = gtk_tree_model_sort_elt_get_path (parent_level, - parent_elt); - str = gtk_tree_path_to_string (level); - gtk_tree_path_free (level); - - g_warning ("%s: There is a discrepancy between the sort model " - "and the child model. The child model is " - "advertising a wrong length for level %s:.", - G_STRLOC, str); - g_free (str); - } - else - { - g_warning ("%s: There is a discrepancy between the sort model " - "and the child model. The child model is " - "advertising a wrong length for the root level.", - G_STRLOC); - } - - return; - } - } - - sort_elt->siter = g_sequence_append (new_level->seq, sort_elt); - } - - /* sort level */ - gtk_tree_model_sort_sort_level (tree_model_sort, new_level, - FALSE, FALSE); -} - -static void -gtk_tree_model_sort_free_level (GtkTreeModelSort *tree_model_sort, - SortLevel *sort_level, - gboolean unref) -{ - GtkTreeModelSortPrivate *priv = tree_model_sort->priv; - GSequenceIter *siter; - GSequenceIter *end_siter; - - g_assert (sort_level); - - end_siter = g_sequence_get_end_iter (sort_level->seq); - for (siter = g_sequence_get_begin_iter (sort_level->seq); - siter != end_siter; - siter = g_sequence_iter_next (siter)) - { - SortElt *elt = g_sequence_get (siter); - - if (elt->children) - gtk_tree_model_sort_free_level (tree_model_sort, - elt->children, unref); - } - - if (sort_level->ref_count == 0) - { - SortLevel *parent_level = sort_level->parent_level; - SortElt *parent_elt = sort_level->parent_elt; - - while (parent_level) - { - parent_elt->zero_ref_count--; - - parent_elt = parent_level->parent_elt; - parent_level = parent_level->parent_level; - } - - if (sort_level != priv->root) - priv->zero_ref_count--; - } - - if (sort_level->parent_elt) - { - if (unref) - { - GtkTreeIter parent_iter; - - parent_iter.stamp = tree_model_sort->priv->stamp; - parent_iter.user_data = sort_level->parent_level; - parent_iter.user_data2 = sort_level->parent_elt; - - gtk_tree_model_sort_unref_node (GTK_TREE_MODEL (tree_model_sort), - &parent_iter); - } - - sort_level->parent_elt->children = NULL; - } - else - priv->root = NULL; - - g_sequence_free (sort_level->seq); - sort_level->seq = NULL; - - g_free (sort_level); - sort_level = NULL; -} - -static void -gtk_tree_model_sort_increment_stamp (GtkTreeModelSort *tree_model_sort) -{ - GtkTreeModelSortPrivate *priv = tree_model_sort->priv; - - do - { - priv->stamp++; - } - while (priv->stamp == 0); - - gtk_tree_model_sort_clear_cache (tree_model_sort); -} - -static void -gtk_tree_model_sort_clear_cache_helper_iter (gpointer data, - gpointer user_data) -{ - GtkTreeModelSort *tree_model_sort = user_data; - SortElt *elt = data; - - if (elt->zero_ref_count > 0) - gtk_tree_model_sort_clear_cache_helper (tree_model_sort, elt->children); -} - -static void -gtk_tree_model_sort_clear_cache_helper (GtkTreeModelSort *tree_model_sort, - SortLevel *level) -{ - g_assert (level != NULL); - - g_sequence_foreach (level->seq, gtk_tree_model_sort_clear_cache_helper_iter, - tree_model_sort); - - if (level->ref_count == 0 && level != tree_model_sort->priv->root) - gtk_tree_model_sort_free_level (tree_model_sort, level, TRUE); -} - -/** - * gtk_tree_model_sort_reset_default_sort_func: - * @tree_model_sort: A `GtkTreeModelSort` - * - * This resets the default sort function to be in the “unsorted” state. That - * is, it is in the same order as the child model. It will re-sort the model - * to be in the same order as the child model only if the `GtkTreeModelSort` - * is in “unsorted” state. - **/ -void -gtk_tree_model_sort_reset_default_sort_func (GtkTreeModelSort *tree_model_sort) -{ - GtkTreeModelSortPrivate *priv = tree_model_sort->priv; - - g_return_if_fail (GTK_IS_TREE_MODEL_SORT (tree_model_sort)); - - if (priv->default_sort_destroy) - { - GDestroyNotify d = priv->default_sort_destroy; - - priv->default_sort_destroy = NULL; - d (priv->default_sort_data); - } - - priv->default_sort_func = NO_SORT_FUNC; - priv->default_sort_data = NULL; - priv->default_sort_destroy = NULL; - - if (priv->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID) - gtk_tree_model_sort_sort (tree_model_sort); - priv->sort_column_id = GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID; -} - -/** - * gtk_tree_model_sort_clear_cache: - * @tree_model_sort: A `GtkTreeModelSort` - * - * This function should almost never be called. It clears the @tree_model_sort - * of any cached iterators that haven’t been reffed with - * gtk_tree_model_ref_node(). This might be useful if the child model being - * sorted is static (and doesn’t change often) and there has been a lot of - * unreffed access to nodes. As a side effect of this function, all unreffed - * iters will be invalid. - **/ -void -gtk_tree_model_sort_clear_cache (GtkTreeModelSort *tree_model_sort) -{ - g_return_if_fail (GTK_IS_TREE_MODEL_SORT (tree_model_sort)); - - if (tree_model_sort->priv->zero_ref_count > 0) - gtk_tree_model_sort_clear_cache_helper (tree_model_sort, (SortLevel *)tree_model_sort->priv->root); -} - -static gboolean -gtk_tree_model_sort_iter_is_valid_helper (GtkTreeIter *iter, - SortLevel *level) -{ - GSequenceIter *siter; - GSequenceIter *end_siter; - - end_siter = g_sequence_get_end_iter (level->seq); - for (siter = g_sequence_get_begin_iter (level->seq); - siter != end_siter; siter = g_sequence_iter_next (siter)) - { - SortElt *elt = g_sequence_get (siter); - - if (iter->user_data == level && iter->user_data2 == elt) - return TRUE; - - if (elt->children) - if (gtk_tree_model_sort_iter_is_valid_helper (iter, elt->children)) - return TRUE; - } - - return FALSE; -} - -/** - * gtk_tree_model_sort_iter_is_valid: - * @tree_model_sort: A `GtkTreeModelSort`. - * @iter: A `GtkTreeIter` - * - * > This function is slow. Only use it for debugging and/or testing - * > purposes. - * - * Checks if the given iter is a valid iter for this `GtkTreeModelSort`. - * - * Returns: %TRUE if the iter is valid, %FALSE if the iter is invalid. - **/ -gboolean -gtk_tree_model_sort_iter_is_valid (GtkTreeModelSort *tree_model_sort, - GtkTreeIter *iter) -{ - g_return_val_if_fail (GTK_IS_TREE_MODEL_SORT (tree_model_sort), FALSE); - g_return_val_if_fail (iter != NULL, FALSE); - - if (!VALID_ITER (iter, tree_model_sort)) - return FALSE; - - return gtk_tree_model_sort_iter_is_valid_helper (iter, - tree_model_sort->priv->root); -} diff --git a/gtk/gtktreemodelsort.h b/gtk/gtktreemodelsort.h deleted file mode 100644 index 8cde66ea97..0000000000 --- a/gtk/gtktreemodelsort.h +++ /dev/null @@ -1,92 +0,0 @@ -/* gtktreemodelsort.h - * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#ifndef __GTK_TREE_MODEL_SORT_H__ -#define __GTK_TREE_MODEL_SORT_H__ - -#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) -#error "Only can be included directly." -#endif - -#include -#include -#include - -G_BEGIN_DECLS - -#define GTK_TYPE_TREE_MODEL_SORT (gtk_tree_model_sort_get_type ()) -#define GTK_TREE_MODEL_SORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_TREE_MODEL_SORT, GtkTreeModelSort)) -#define GTK_TREE_MODEL_SORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_TREE_MODEL_SORT, GtkTreeModelSortClass)) -#define GTK_IS_TREE_MODEL_SORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_TREE_MODEL_SORT)) -#define GTK_IS_TREE_MODEL_SORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_TREE_MODEL_SORT)) -#define GTK_TREE_MODEL_SORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_TREE_MODEL_SORT, GtkTreeModelSortClass)) - -typedef struct _GtkTreeModelSort GtkTreeModelSort; -typedef struct _GtkTreeModelSortClass GtkTreeModelSortClass; -typedef struct _GtkTreeModelSortPrivate GtkTreeModelSortPrivate; - -struct _GtkTreeModelSort -{ - GObject parent; - - /* < private > */ - GtkTreeModelSortPrivate *priv; -}; - -struct _GtkTreeModelSortClass -{ - GObjectClass parent_class; - - /* < private > */ - gpointer padding[8]; -}; - - -GDK_AVAILABLE_IN_ALL -GType gtk_tree_model_sort_get_type (void) G_GNUC_CONST; -GDK_AVAILABLE_IN_ALL -GtkTreeModel *gtk_tree_model_sort_new_with_model (GtkTreeModel *child_model); - -GDK_AVAILABLE_IN_ALL -GtkTreeModel *gtk_tree_model_sort_get_model (GtkTreeModelSort *tree_model); -GDK_AVAILABLE_IN_ALL -GtkTreePath *gtk_tree_model_sort_convert_child_path_to_path (GtkTreeModelSort *tree_model_sort, - GtkTreePath *child_path); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_model_sort_convert_child_iter_to_iter (GtkTreeModelSort *tree_model_sort, - GtkTreeIter *sort_iter, - GtkTreeIter *child_iter); -GDK_AVAILABLE_IN_ALL -GtkTreePath *gtk_tree_model_sort_convert_path_to_child_path (GtkTreeModelSort *tree_model_sort, - GtkTreePath *sorted_path); -GDK_AVAILABLE_IN_ALL -void gtk_tree_model_sort_convert_iter_to_child_iter (GtkTreeModelSort *tree_model_sort, - GtkTreeIter *child_iter, - GtkTreeIter *sorted_iter); -GDK_AVAILABLE_IN_ALL -void gtk_tree_model_sort_reset_default_sort_func (GtkTreeModelSort *tree_model_sort); -GDK_AVAILABLE_IN_ALL -void gtk_tree_model_sort_clear_cache (GtkTreeModelSort *tree_model_sort); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_model_sort_iter_is_valid (GtkTreeModelSort *tree_model_sort, - GtkTreeIter *iter); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkTreeModelSort, g_object_unref) - -G_END_DECLS - -#endif /* __GTK_TREE_MODEL_SORT_H__ */ diff --git a/gtk/gtktreepopover.c b/gtk/gtktreepopover.c deleted file mode 100644 index 992c4574a5..0000000000 --- a/gtk/gtktreepopover.c +++ /dev/null @@ -1,910 +0,0 @@ -/* - * Copyright © 2019 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the licence, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see . - * - * Author: Matthias Clasen - */ - -#include "config.h" - -#include "gtktreepopoverprivate.h" - -#include "gtktreemodel.h" -#include "gtkcellarea.h" -#include "gtkcelllayout.h" -#include "gtkcellview.h" -#include "gtkprivate.h" -#include "gtkgizmoprivate.h" -#include "gtkwidgetprivate.h" -#include "gtkbuiltiniconprivate.h" -#include "gtkscrolledwindow.h" -#include "gtkviewport.h" - -// TODO -// positioning + sizing - -struct _GtkTreePopover -{ - GtkPopover parent_instance; - - GtkTreeModel *model; - - GtkCellArea *area; - GtkCellAreaContext *context; - - gulong size_changed_id; - gulong row_inserted_id; - gulong row_deleted_id; - gulong row_changed_id; - gulong row_reordered_id; - gulong apply_attributes_id; - - GtkTreeViewRowSeparatorFunc row_separator_func; - gpointer row_separator_data; - GDestroyNotify row_separator_destroy; - - GtkWidget *active_item; -}; - -enum { - PROP_0, - PROP_MODEL, - PROP_CELL_AREA, - - NUM_PROPERTIES -}; - -enum { - MENU_ACTIVATE, - NUM_SIGNALS -}; - -static guint signals[NUM_SIGNALS]; - -static void gtk_tree_popover_cell_layout_init (GtkCellLayoutIface *iface); -static void gtk_tree_popover_set_area (GtkTreePopover *popover, - GtkCellArea *area); -static void rebuild_menu (GtkTreePopover *popover); -static void context_size_changed_cb (GtkCellAreaContext *context, - GParamSpec *pspec, - GtkWidget *popover); -static GtkWidget * gtk_tree_popover_create_item (GtkTreePopover *popover, - GtkTreePath *path, - GtkTreeIter *iter, - gboolean header_item); -static GtkWidget * gtk_tree_popover_get_path_item (GtkTreePopover *popover, - GtkTreePath *search); -static void gtk_tree_popover_set_active_item (GtkTreePopover *popover, - GtkWidget *item); - -G_DEFINE_TYPE_WITH_CODE (GtkTreePopover, gtk_tree_popover, GTK_TYPE_POPOVER, - G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT, - gtk_tree_popover_cell_layout_init)); - -static void -gtk_tree_popover_constructed (GObject *object) -{ - GtkTreePopover *popover = GTK_TREE_POPOVER (object); - - G_OBJECT_CLASS (gtk_tree_popover_parent_class)->constructed (object); - - if (!popover->area) - { - GtkCellArea *area = gtk_cell_area_box_new (); - gtk_tree_popover_set_area (popover, area); - } - - popover->context = gtk_cell_area_create_context (popover->area); - - popover->size_changed_id = g_signal_connect (popover->context, "notify", - G_CALLBACK (context_size_changed_cb), popover); -} - -static void -gtk_tree_popover_dispose (GObject *object) -{ - GtkTreePopover *popover = GTK_TREE_POPOVER (object); - - gtk_tree_popover_set_model (popover, NULL); - gtk_tree_popover_set_area (popover, NULL); - - if (popover->context) - { - g_signal_handler_disconnect (popover->context, popover->size_changed_id); - popover->size_changed_id = 0; - - g_clear_object (&popover->context); - } - - G_OBJECT_CLASS (gtk_tree_popover_parent_class)->dispose (object); -} - -static void -gtk_tree_popover_finalize (GObject *object) -{ - GtkTreePopover *popover = GTK_TREE_POPOVER (object); - - if (popover->row_separator_destroy) - popover->row_separator_destroy (popover->row_separator_data); - - G_OBJECT_CLASS (gtk_tree_popover_parent_class)->finalize (object); -} - -static void -gtk_tree_popover_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - GtkTreePopover *popover = GTK_TREE_POPOVER (object); - - switch (prop_id) - { - case PROP_MODEL: - gtk_tree_popover_set_model (popover, g_value_get_object (value)); - break; - - case PROP_CELL_AREA: - gtk_tree_popover_set_area (popover, g_value_get_object (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gtk_tree_popover_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GtkTreePopover *popover = GTK_TREE_POPOVER (object); - - switch (prop_id) - { - case PROP_MODEL: - g_value_set_object (value, popover->model); - break; - - case PROP_CELL_AREA: - g_value_set_object (value, popover->area); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gtk_tree_popover_class_init (GtkTreePopoverClass *class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (class); - - object_class->constructed = gtk_tree_popover_constructed; - object_class->dispose = gtk_tree_popover_dispose; - object_class->finalize = gtk_tree_popover_finalize; - object_class->set_property = gtk_tree_popover_set_property; - object_class->get_property = gtk_tree_popover_get_property; - - g_object_class_install_property (object_class, - PROP_MODEL, - g_param_spec_object ("model", NULL, NULL, - GTK_TYPE_TREE_MODEL, - GTK_PARAM_READWRITE)); - - g_object_class_install_property (object_class, - PROP_CELL_AREA, - g_param_spec_object ("cell-area", NULL, NULL, - GTK_TYPE_CELL_AREA, - GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - - signals[MENU_ACTIVATE] = - g_signal_new (I_("menu-activate"), - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, - NULL, NULL, - NULL, - G_TYPE_NONE, 1, G_TYPE_STRING); -} - -static GtkWidget * -gtk_tree_popover_get_stack (GtkTreePopover *popover) -{ - GtkWidget *sw = gtk_popover_get_child (GTK_POPOVER (popover)); - GtkWidget *vp = gtk_scrolled_window_get_child (GTK_SCROLLED_WINDOW (sw)); - GtkWidget *stack = gtk_viewport_get_child (GTK_VIEWPORT (vp)); - - return stack; -} - -static void -gtk_tree_popover_add_submenu (GtkTreePopover *popover, - GtkWidget *submenu, - const char *name) -{ - GtkWidget *stack = gtk_tree_popover_get_stack (popover); - gtk_stack_add_named (GTK_STACK (stack), submenu, name); -} - -static GtkWidget * -gtk_tree_popover_get_submenu (GtkTreePopover *popover, - const char *name) -{ - GtkWidget *stack = gtk_tree_popover_get_stack (popover); - return gtk_stack_get_child_by_name (GTK_STACK (stack), name); -} - -void -gtk_tree_popover_open_submenu (GtkTreePopover *popover, - const char *name) -{ - GtkWidget *stack = gtk_tree_popover_get_stack (popover); - gtk_stack_set_visible_child_name (GTK_STACK (stack), name); -} - -static void -gtk_tree_popover_init (GtkTreePopover *popover) -{ - GtkWidget *sw; - GtkWidget *stack; - - sw = gtk_scrolled_window_new (); - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); - gtk_scrolled_window_set_propagate_natural_height (GTK_SCROLLED_WINDOW (sw), TRUE); - gtk_popover_set_child (GTK_POPOVER (popover), sw); - - stack = gtk_stack_new (); - gtk_stack_set_vhomogeneous (GTK_STACK (stack), FALSE); - gtk_stack_set_transition_type (GTK_STACK (stack), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT); - gtk_stack_set_interpolate_size (GTK_STACK (stack), TRUE); - gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), stack); - - gtk_widget_add_css_class (GTK_WIDGET (popover), "menu"); -} - -static GtkCellArea * -gtk_tree_popover_cell_layout_get_area (GtkCellLayout *layout) -{ - return GTK_TREE_POPOVER (layout)->area; -} - -static void -gtk_tree_popover_cell_layout_init (GtkCellLayoutIface *iface) -{ - iface->get_area = gtk_tree_popover_cell_layout_get_area; -} - -static void -insert_at_position (GtkBox *box, - GtkWidget *child, - int position) -{ - GtkWidget *sibling = NULL; - - if (position > 0) - { - int i; - - sibling = gtk_widget_get_first_child (GTK_WIDGET (box)); - for (i = 1; i < position; i++) - sibling = gtk_widget_get_next_sibling (sibling); - } - - gtk_box_insert_child_after (box, child, sibling); -} - -static GtkWidget * -ensure_submenu (GtkTreePopover *popover, - GtkTreePath *path) -{ - GtkWidget *box; - char *name; - - if (path) - name = gtk_tree_path_to_string (path); - else - name = NULL; - - box = gtk_tree_popover_get_submenu (popover, name ? name : "main"); - if (!box) - { - box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); - gtk_tree_popover_add_submenu (popover, box, name ? name : "main"); - if (path) - { - GtkTreeIter iter; - GtkWidget *item; - gtk_tree_model_get_iter (popover->model, &iter, path); - item = gtk_tree_popover_create_item (popover, path, &iter, TRUE); - gtk_box_append (GTK_BOX (box), item); - gtk_box_append (GTK_BOX (box), gtk_separator_new (GTK_ORIENTATION_HORIZONTAL)); - } - - } - - g_free (name); - - return box; -} - -static void -row_inserted_cb (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - GtkTreePopover *popover) -{ - int *indices, depth, index; - GtkWidget *item; - GtkWidget *box; - - indices = gtk_tree_path_get_indices (path); - depth = gtk_tree_path_get_depth (path); - index = indices[depth - 1]; - - item = gtk_tree_popover_create_item (popover, path, iter, FALSE); - if (depth == 1) - { - box = ensure_submenu (popover, NULL); - insert_at_position (GTK_BOX (box), item, index); - } - else - { - GtkTreePath *ppath; - - ppath = gtk_tree_path_copy (path); - gtk_tree_path_up (ppath); - - box = ensure_submenu (popover, ppath); - insert_at_position (GTK_BOX (box), item, index + 2); - - gtk_tree_path_free (ppath); - } - - gtk_cell_area_context_reset (popover->context); -} - -static void -row_deleted_cb (GtkTreeModel *model, - GtkTreePath *path, - GtkTreePopover *popover) -{ - GtkWidget *item; - - item = gtk_tree_popover_get_path_item (popover, path); - - if (item) - { - gtk_widget_unparent (item); - gtk_cell_area_context_reset (popover->context); - } -} - -static void -row_changed_cb (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - GtkTreePopover *popover) -{ - gboolean is_separator = FALSE; - GtkWidget *item; - int *indices, depth, index; - - item = gtk_tree_popover_get_path_item (popover, path); - - if (!item) - return; - - indices = gtk_tree_path_get_indices (path); - depth = gtk_tree_path_get_depth (path); - index = indices[depth - 1]; - - if (popover->row_separator_func) - is_separator = popover->row_separator_func (model, iter, popover->row_separator_data); - - if (is_separator != GTK_IS_SEPARATOR (item)) - { - GtkWidget *box = gtk_widget_get_parent (item); - - gtk_box_remove (GTK_BOX (box), item); - - item = gtk_tree_popover_create_item (popover, path, iter, FALSE); - - if (depth == 1) - insert_at_position (GTK_BOX (box), item, index); - else - insert_at_position (GTK_BOX (box), item, index + 2); - } -} - -static void -row_reordered_cb (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - int *new_order, - GtkTreePopover *popover) -{ - rebuild_menu (popover); -} - -static void -context_size_changed_cb (GtkCellAreaContext *context, - GParamSpec *pspec, - GtkWidget *popover) -{ - if (!strcmp (pspec->name, "minimum-width") || - !strcmp (pspec->name, "natural-width") || - !strcmp (pspec->name, "minimum-height") || - !strcmp (pspec->name, "natural-height")) - gtk_widget_queue_resize (popover); -} - -static gboolean -area_is_sensitive (GtkCellArea *area) -{ - GList *cells, *list; - gboolean sensitive = FALSE; - - cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (area)); - - for (list = cells; list; list = list->next) - { - g_object_get (list->data, "sensitive", &sensitive, NULL); - - if (sensitive) - break; - } - g_list_free (cells); - - return sensitive; -} - -static GtkWidget * -gtk_tree_popover_get_path_item (GtkTreePopover *popover, - GtkTreePath *search) -{ - GtkWidget *stack = gtk_tree_popover_get_stack (popover); - GtkWidget *item = NULL; - GtkWidget *stackchild; - GtkWidget *child; - - for (stackchild = gtk_widget_get_first_child (stack); - stackchild != NULL; - stackchild = gtk_widget_get_next_sibling (stackchild)) - { - for (child = gtk_widget_get_first_child (stackchild); - !item && child; - child = gtk_widget_get_next_sibling (child)) - { - GtkTreePath *path = NULL; - - if (GTK_IS_SEPARATOR (child)) - { - GtkTreeRowReference *row = g_object_get_data (G_OBJECT (child), "gtk-tree-path"); - - if (row) - { - path = gtk_tree_row_reference_get_path (row); - if (!path) - item = child; - } - } - else - { - GtkWidget *view = GTK_WIDGET (g_object_get_data (G_OBJECT (child), "view")); - - path = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (view)); - - if (!path) - item = child; - } - - if (path) - { - if (gtk_tree_path_compare (search, path) == 0) - item = child; - gtk_tree_path_free (path); - } - } - } - - return item; -} - -static void -area_apply_attributes_cb (GtkCellArea *area, - GtkTreeModel *tree_model, - GtkTreeIter *iter, - gboolean is_expander, - gboolean is_expanded, - GtkTreePopover *popover) -{ - GtkTreePath*path; - GtkWidget *item; - gboolean sensitive; - GtkTreeIter dummy; - gboolean has_submenu = FALSE; - - if (gtk_tree_model_iter_children (popover->model, &dummy, iter)) - has_submenu = TRUE; - - path = gtk_tree_model_get_path (tree_model, iter); - item = gtk_tree_popover_get_path_item (popover, path); - - if (item) - { - sensitive = area_is_sensitive (popover->area); - gtk_widget_set_sensitive (item, sensitive || has_submenu); - } - - gtk_tree_path_free (path); -} - -static void -gtk_tree_popover_set_area (GtkTreePopover *popover, - GtkCellArea *area) -{ - if (popover->area) - { - g_signal_handler_disconnect (popover->area, popover->apply_attributes_id); - popover->apply_attributes_id = 0; - g_clear_object (&popover->area); - } - - popover->area = area; - - if (popover->area) - { - g_object_ref_sink (popover->area); - popover->apply_attributes_id = g_signal_connect (popover->area, "apply-attributes", - G_CALLBACK (area_apply_attributes_cb), popover); - } -} - -static void -activate_item (GtkWidget *item, - GtkTreePopover *popover) -{ - GtkCellView *view; - GtkTreePath *path; - char *path_str; - gboolean is_header = FALSE; - gboolean has_submenu = FALSE; - - is_header = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item), "is-header")); - - view = GTK_CELL_VIEW (g_object_get_data (G_OBJECT (item), "view")); - - path = gtk_cell_view_get_displayed_row (view); - - if (is_header) - { - gtk_tree_path_up (path); - } - else - { - GtkTreeIter iter; - GtkTreeIter dummy; - - gtk_tree_model_get_iter (popover->model, &iter, path); - if (gtk_tree_model_iter_children (popover->model, &dummy, &iter)) - has_submenu = TRUE; - } - - path_str = gtk_tree_path_to_string (path); - - if (is_header || has_submenu) - { - gtk_tree_popover_open_submenu (popover, path_str ? path_str : "main"); - } - else - { - g_signal_emit (popover, signals[MENU_ACTIVATE], 0, path_str); - gtk_popover_popdown (GTK_POPOVER (popover)); - } - - g_free (path_str); - gtk_tree_path_free (path); -} - -static void -item_activated_cb (GtkGesture *gesture, - guint n_press, - double x, - double y, - GtkTreePopover *popover) -{ - GtkWidget *item = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture)); - activate_item (item, popover); -} - -static void -enter_cb (GtkEventController *controller, - double x, - double y, - GtkTreePopover *popover) -{ - GtkWidget *item; - item = gtk_event_controller_get_widget (controller); - - gtk_tree_popover_set_active_item (popover, item); -} - -static void -enter_focus_cb (GtkEventController *controller, - GtkTreePopover *popover) -{ - GtkWidget *item = gtk_event_controller_get_widget (controller); - - gtk_tree_popover_set_active_item (popover, item); -} - -static gboolean -activate_shortcut (GtkWidget *widget, - GVariant *args, - gpointer user_data) -{ - activate_item (widget, user_data); - return TRUE; -} - -static GtkWidget * -gtk_tree_popover_create_item (GtkTreePopover *popover, - GtkTreePath *path, - GtkTreeIter *iter, - gboolean header_item) -{ - GtkWidget *item, *view; - gboolean is_separator = FALSE; - - if (popover->row_separator_func) - is_separator = popover->row_separator_func (popover->model, iter, popover->row_separator_data); - - if (is_separator) - { - item = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL); - g_object_set_data_full (G_OBJECT (item), "gtk-tree-path", - gtk_tree_row_reference_new (popover->model, path), - (GDestroyNotify)gtk_tree_row_reference_free); - } - else - { - GtkEventController *controller; - GtkTreeIter dummy; - gboolean has_submenu = FALSE; - GtkWidget *indicator; - - if (!header_item && - gtk_tree_model_iter_children (popover->model, &dummy, iter)) - has_submenu = TRUE; - - view = gtk_cell_view_new_with_context (popover->area, popover->context); - gtk_cell_view_set_model (GTK_CELL_VIEW (view), popover->model); - gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (view), path); - gtk_widget_set_hexpand (view, TRUE); - - item = gtk_gizmo_new ("modelbutton", NULL, NULL, NULL, NULL, - (GtkGizmoFocusFunc)gtk_widget_focus_self, - (GtkGizmoGrabFocusFunc)gtk_widget_grab_focus_self); - gtk_widget_set_layout_manager (item, gtk_box_layout_new (GTK_ORIENTATION_HORIZONTAL)); - gtk_widget_set_focusable (item, TRUE); - gtk_widget_add_css_class (item, "flat"); - - if (header_item) - { - indicator = gtk_builtin_icon_new ("arrow"); - gtk_widget_add_css_class (indicator, "left"); - gtk_widget_set_parent (indicator, item); - } - - gtk_widget_set_parent (view, item); - - indicator = gtk_builtin_icon_new (has_submenu ? "arrow" : "none"); - gtk_widget_add_css_class (indicator, "right"); - gtk_widget_set_parent (indicator, item); - - controller = GTK_EVENT_CONTROLLER (gtk_gesture_click_new ()); - g_signal_connect (controller, "pressed", G_CALLBACK (item_activated_cb), popover); - gtk_widget_add_controller (item, GTK_EVENT_CONTROLLER (controller)); - - controller = gtk_event_controller_motion_new (); - g_signal_connect (controller, "enter", G_CALLBACK (enter_cb), popover); - gtk_widget_add_controller (item, controller); - - controller = gtk_event_controller_focus_new (); - g_signal_connect (controller, "enter", G_CALLBACK (enter_focus_cb), popover); - gtk_widget_add_controller (item, controller); - - { - const guint activate_keyvals[] = { GDK_KEY_space, GDK_KEY_KP_Space, - GDK_KEY_Return, GDK_KEY_ISO_Enter, - GDK_KEY_KP_Enter }; - GtkShortcutTrigger *trigger; - GtkShortcut *shortcut; - - trigger = g_object_ref (gtk_never_trigger_get ()); - for (int i = 0; i < G_N_ELEMENTS (activate_keyvals); i++) - trigger = gtk_alternative_trigger_new (gtk_keyval_trigger_new (activate_keyvals[i], 0), trigger); - - shortcut = gtk_shortcut_new (trigger, gtk_callback_action_new (activate_shortcut, popover, NULL)); - controller = gtk_shortcut_controller_new (); - gtk_shortcut_controller_add_shortcut (GTK_SHORTCUT_CONTROLLER (controller), shortcut); - gtk_widget_add_controller (item, controller); - } - - g_object_set_data (G_OBJECT (item), "is-header", GINT_TO_POINTER (header_item)); - g_object_set_data (G_OBJECT (item), "view", view); - } - - return item; -} - -static void -populate (GtkTreePopover *popover, - GtkTreeIter *parent) -{ - GtkTreeIter iter; - gboolean valid = FALSE; - - if (!popover->model) - return; - - valid = gtk_tree_model_iter_children (popover->model, &iter, parent); - - while (valid) - { - GtkTreePath *path; - - path = gtk_tree_model_get_path (popover->model, &iter); - row_inserted_cb (popover->model, path, &iter, popover); - - populate (popover, &iter); - - valid = gtk_tree_model_iter_next (popover->model, &iter); - gtk_tree_path_free (path); - } -} - -static void -gtk_tree_popover_populate (GtkTreePopover *popover) -{ - populate (popover, NULL); -} - -static void -rebuild_menu (GtkTreePopover *popover) -{ - GtkWidget *stack; - GtkWidget *child; - - stack = gtk_tree_popover_get_stack (popover); - while ((child = gtk_widget_get_first_child (stack))) - gtk_stack_remove (GTK_STACK (stack), child); - - if (popover->model) - gtk_tree_popover_populate (popover); -} - -void -gtk_tree_popover_set_model (GtkTreePopover *popover, - GtkTreeModel *model) -{ - if (popover->model == model) - return; - - if (popover->model) - { - g_signal_handler_disconnect (popover->model, popover->row_inserted_id); - g_signal_handler_disconnect (popover->model, popover->row_deleted_id); - g_signal_handler_disconnect (popover->model, popover->row_changed_id); - g_signal_handler_disconnect (popover->model, popover->row_reordered_id); - popover->row_inserted_id = 0; - popover->row_deleted_id = 0; - popover->row_changed_id = 0; - popover->row_reordered_id = 0; - - g_object_unref (popover->model); - } - - popover->model = model; - - if (popover->model) - { - g_object_ref (popover->model); - - popover->row_inserted_id = g_signal_connect (popover->model, "row-inserted", - G_CALLBACK (row_inserted_cb), popover); - popover->row_deleted_id = g_signal_connect (popover->model, "row-deleted", - G_CALLBACK (row_deleted_cb), popover); - popover->row_changed_id = g_signal_connect (popover->model, "row-changed", - G_CALLBACK (row_changed_cb), popover); - popover->row_reordered_id = g_signal_connect (popover->model, "rows-reordered", - G_CALLBACK (row_reordered_cb), popover); - } - - rebuild_menu (popover); -} - -void -gtk_tree_popover_set_row_separator_func (GtkTreePopover *popover, - GtkTreeViewRowSeparatorFunc func, - gpointer data, - GDestroyNotify destroy) -{ - if (popover->row_separator_destroy) - popover->row_separator_destroy (popover->row_separator_data); - - popover->row_separator_func = func; - popover->row_separator_data = data; - popover->row_separator_destroy = destroy; - - rebuild_menu (popover); -} - -static void -gtk_tree_popover_set_active_item (GtkTreePopover *popover, - GtkWidget *item) -{ - if (popover->active_item == item) - return; - - if (popover->active_item) - { - gtk_widget_unset_state_flags (popover->active_item, GTK_STATE_FLAG_SELECTED); - g_object_remove_weak_pointer (G_OBJECT (popover->active_item), (gpointer *)&popover->active_item); - } - - popover->active_item = item; - - if (popover->active_item) - { - g_object_add_weak_pointer (G_OBJECT (popover->active_item), (gpointer *)&popover->active_item); - gtk_widget_set_state_flags (popover->active_item, GTK_STATE_FLAG_SELECTED, FALSE); - } -} - -void -gtk_tree_popover_set_active (GtkTreePopover *popover, - int item) -{ - GtkWidget *box; - GtkWidget *child; - int pos; - - if (item == -1) - { - gtk_tree_popover_set_active_item (popover, NULL); - return; - } - - box = gtk_tree_popover_get_submenu (popover, "main"); - if (!box) - return; - - for (child = gtk_widget_get_first_child (box), pos = 0; - child; - child = gtk_widget_get_next_sibling (child), pos++) - { - if (pos == item) - { - gtk_tree_popover_set_active_item (popover, child); - break; - } - } -} - diff --git a/gtk/gtktreepopoverprivate.h b/gtk/gtktreepopoverprivate.h deleted file mode 100644 index 2656f90a1d..0000000000 --- a/gtk/gtktreepopoverprivate.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright © 2019 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the licence, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see . - * - * Author: Matthias Clasen - */ - -#ifndef __GTK_TREE_POPOVER_PRIVATE_H__ -#define __GTK_TREE_POPOVER_PRIVATE_H__ - -#include - -G_BEGIN_DECLS - -#define GTK_TYPE_TREE_POPOVER (gtk_tree_popover_get_type ()) -G_DECLARE_FINAL_TYPE (GtkTreePopover, gtk_tree_popover, GTK, TREE_POPOVER, GtkPopover) - -void gtk_tree_popover_set_model (GtkTreePopover *popover, - GtkTreeModel *model); -void gtk_tree_popover_set_row_separator_func (GtkTreePopover *popover, - GtkTreeViewRowSeparatorFunc func, - gpointer data, - GDestroyNotify destroy); -void gtk_tree_popover_set_active (GtkTreePopover *popover, - int item); -void gtk_tree_popover_open_submenu (GtkTreePopover *popover, - const char *name); - -G_END_DECLS - -#endif /* __GTK_TREE_POPOVER_PRIVATE_H__ */ diff --git a/gtk/gtktreeprivate.h b/gtk/gtktreeprivate.h deleted file mode 100644 index 37dafa8e01..0000000000 --- a/gtk/gtktreeprivate.h +++ /dev/null @@ -1,146 +0,0 @@ -/* gtktreeprivate.h - * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#ifndef __GTK_TREE_PRIVATE_H__ -#define __GTK_TREE_PRIVATE_H__ - - -#include -#include -#include - -G_BEGIN_DECLS - -#define TREE_VIEW_DRAG_WIDTH 6 - -typedef enum -{ - GTK_TREE_SELECT_MODE_TOGGLE = 1 << 0, - GTK_TREE_SELECT_MODE_EXTEND = 1 << 1 -} -GtkTreeSelectMode; - -/* functions that shouldn't be exported */ -void _gtk_tree_selection_internal_select_node (GtkTreeSelection *selection, - GtkTreeRBNode *node, - GtkTreeRBTree *tree, - GtkTreePath *path, - GtkTreeSelectMode mode, - gboolean override_browse_mode); -void _gtk_tree_selection_emit_changed (GtkTreeSelection *selection); -gboolean _gtk_tree_view_find_node (GtkTreeView *tree_view, - GtkTreePath *path, - GtkTreeRBTree **tree, - GtkTreeRBNode **node); -gboolean _gtk_tree_view_get_cursor_node (GtkTreeView *tree_view, - GtkTreeRBTree **tree, - GtkTreeRBNode **node); -GtkTreePath *_gtk_tree_path_new_from_rbtree (GtkTreeRBTree *tree, - GtkTreeRBNode *node); - -void _gtk_tree_view_add_editable (GtkTreeView *tree_view, - GtkTreeViewColumn *column, - GtkTreePath *path, - GtkCellEditable *cell_editable, - GdkRectangle *cell_area); -void _gtk_tree_view_remove_editable (GtkTreeView *tree_view, - GtkTreeViewColumn *column, - GtkCellEditable *cell_editable); - -void _gtk_tree_view_install_mark_rows_col_dirty (GtkTreeView *tree_view, - gboolean install_handler); -void _gtk_tree_view_column_autosize (GtkTreeView *tree_view, - GtkTreeViewColumn *column); - -void _gtk_tree_view_get_row_separator_func (GtkTreeView *tree_view, - GtkTreeViewRowSeparatorFunc *func, - gpointer *data); -GtkTreePath *_gtk_tree_view_get_anchor_path (GtkTreeView *tree_view); -void _gtk_tree_view_set_anchor_path (GtkTreeView *tree_view, - GtkTreePath *anchor_path); -GtkTreeRBTree * _gtk_tree_view_get_rbtree (GtkTreeView *tree_view); - -GtkTreeViewColumn *_gtk_tree_view_get_focus_column (GtkTreeView *tree_view); -void _gtk_tree_view_set_focus_column (GtkTreeView *tree_view, - GtkTreeViewColumn *column); - -GtkTreeSelection* _gtk_tree_selection_new (void); -GtkTreeSelection* _gtk_tree_selection_new_with_tree_view (GtkTreeView *tree_view); -void _gtk_tree_selection_set_tree_view (GtkTreeSelection *selection, - GtkTreeView *tree_view); -gboolean _gtk_tree_selection_row_is_selectable (GtkTreeSelection *selection, - GtkTreeRBNode *node, - GtkTreePath *path); - - -void _gtk_tree_view_column_realize_button (GtkTreeViewColumn *column); - -void _gtk_tree_view_column_set_tree_view (GtkTreeViewColumn *column, - GtkTreeView *tree_view); -int _gtk_tree_view_column_request_width (GtkTreeViewColumn *tree_column); -void _gtk_tree_view_column_allocate (GtkTreeViewColumn *tree_column, - int x_offset, - int width, - int height); -void _gtk_tree_view_column_unset_model (GtkTreeViewColumn *column, - GtkTreeModel *old_model); -void _gtk_tree_view_column_unset_tree_view (GtkTreeViewColumn *column); -void _gtk_tree_view_column_start_drag (GtkTreeView *tree_view, - GtkTreeViewColumn *column, - GdkDevice *device); -gboolean _gtk_tree_view_column_cell_event (GtkTreeViewColumn *tree_column, - GdkEvent *event, - const GdkRectangle *cell_area, - guint flags); -gboolean _gtk_tree_view_column_has_editable_cell(GtkTreeViewColumn *column); -GtkCellRenderer *_gtk_tree_view_column_get_edited_cell (GtkTreeViewColumn *column); -GtkCellRenderer *_gtk_tree_view_column_get_cell_at_pos (GtkTreeViewColumn *column, - GdkRectangle *cell_area, - GdkRectangle *background_area, - int x, - int y); -gboolean _gtk_tree_view_column_is_blank_at_pos (GtkTreeViewColumn *column, - GdkRectangle *cell_area, - GdkRectangle *background_area, - int x, - int y); - -void gtk_tree_view_column_cell_snapshot (GtkTreeViewColumn *tree_column, - GtkSnapshot *snapshot, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - guint flags, - gboolean draw_focus); -void _gtk_tree_view_column_cell_set_dirty (GtkTreeViewColumn *tree_column, - gboolean install_handler); -gboolean _gtk_tree_view_column_cell_get_dirty (GtkTreeViewColumn *tree_column); - -void _gtk_tree_view_column_push_padding (GtkTreeViewColumn *column, - int padding); -int _gtk_tree_view_column_get_requested_width (GtkTreeViewColumn *column); -int _gtk_tree_view_column_get_drag_x (GtkTreeViewColumn *column); -GtkCellAreaContext *_gtk_tree_view_column_get_context (GtkTreeViewColumn *column); -gboolean _gtk_tree_view_column_coords_in_resize_rect (GtkTreeViewColumn *column, - double x, - double y); - - -G_END_DECLS - - -#endif /* __GTK_TREE_PRIVATE_H__ */ - diff --git a/gtk/gtktreerbtree.c b/gtk/gtktreerbtree.c deleted file mode 100644 index 36d7534133..0000000000 --- a/gtk/gtktreerbtree.c +++ /dev/null @@ -1,1745 +0,0 @@ -/* gtktreerbtree.c - * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#include "config.h" -#include "gtktreerbtreeprivate.h" -#include "gtkdebug.h" - -static GtkTreeRBNode *gtk_tree_rbnode_new (GtkTreeRBTree *tree, - int height); -static void gtk_tree_rbnode_free (GtkTreeRBNode *node); -static void gtk_tree_rbnode_rotate_left (GtkTreeRBTree *tree, - GtkTreeRBNode *node); -static void gtk_tree_rbnode_rotate_right (GtkTreeRBTree *tree, - GtkTreeRBNode *node); -static void gtk_tree_rbtree_insert_fixup (GtkTreeRBTree *tree, - GtkTreeRBNode *node); -static void gtk_tree_rbtree_remove_node_fixup (GtkTreeRBTree *tree, - GtkTreeRBNode *node, - GtkTreeRBNode *parent); -static inline void fixup_validation (GtkTreeRBTree *tree, - GtkTreeRBNode *node); -static inline void fixup_total_count (GtkTreeRBTree *tree, - GtkTreeRBNode *node); -#ifdef G_ENABLE_DEBUG -static void gtk_tree_rbtree_test (const char *where, - GtkTreeRBTree *tree); -static void gtk_tree_rbtree_debug_spew (GtkTreeRBTree *tree, - GString *s); -#endif - -static const GtkTreeRBNode nil = -{ - /* .flags = */ GTK_TREE_RBNODE_BLACK, - - /* rest is NULL */ -}; - -gboolean -gtk_tree_rbtree_is_nil (GtkTreeRBNode *node) -{ - return node == &nil; -} - -static GtkTreeRBNode * -gtk_tree_rbnode_new (GtkTreeRBTree *tree, - int height) -{ - GtkTreeRBNode *node = g_slice_new (GtkTreeRBNode); - - node->left = (GtkTreeRBNode *) &nil; - node->right = (GtkTreeRBNode *) &nil; - node->parent = (GtkTreeRBNode *) &nil; - node->flags = GTK_TREE_RBNODE_RED; - node->total_count = 1; - node->count = 1; - node->children = NULL; - node->offset = height; - return node; -} - -static void -gtk_tree_rbnode_free (GtkTreeRBNode *node) -{ -#ifdef G_ENABLE_DEBUG - if (GTK_DEBUG_CHECK (TREE)) - { - node->left = (gpointer) 0xdeadbeef; - node->right = (gpointer) 0xdeadbeef; - node->parent = (gpointer) 0xdeadbeef; - node->total_count = 56789; - node->offset = 56789; - node->count = 56789; - node->flags = 0; - } -#endif - g_slice_free (GtkTreeRBNode, node); -} - -static void -gtk_tree_rbnode_rotate_left (GtkTreeRBTree *tree, - GtkTreeRBNode *node) -{ - int node_height, right_height; - GtkTreeRBNode *right; - - g_return_if_fail (!gtk_tree_rbtree_is_nil (node)); - g_return_if_fail (!gtk_tree_rbtree_is_nil (node->right)); - - right = node->right; - - node_height = GTK_TREE_RBNODE_GET_HEIGHT (node); - right_height = GTK_TREE_RBNODE_GET_HEIGHT (right); - node->right = right->left; - if (!gtk_tree_rbtree_is_nil (right->left)) - right->left->parent = node; - - right->parent = node->parent; - if (!gtk_tree_rbtree_is_nil (node->parent)) - { - if (node == node->parent->left) - node->parent->left = right; - else - node->parent->right = right; - } - else - { - tree->root = right; - } - - right->left = node; - node->parent = right; - - node->count = 1 + node->left->count + node->right->count; - right->count = 1 + right->left->count + right->right->count; - - node->offset = node_height + node->left->offset + node->right->offset + - (node->children ? node->children->root->offset : 0); - right->offset = right_height + right->left->offset + right->right->offset + - (right->children ? right->children->root->offset : 0); - - fixup_validation (tree, node); - fixup_validation (tree, right); - fixup_total_count (tree, node); - fixup_total_count (tree, right); -} - -static void -gtk_tree_rbnode_rotate_right (GtkTreeRBTree *tree, - GtkTreeRBNode *node) -{ - int node_height, left_height; - GtkTreeRBNode *left; - - g_return_if_fail (!gtk_tree_rbtree_is_nil (node)); - g_return_if_fail (!gtk_tree_rbtree_is_nil (node->left)); - - left = node->left; - - node_height = GTK_TREE_RBNODE_GET_HEIGHT (node); - left_height = GTK_TREE_RBNODE_GET_HEIGHT (left); - - node->left = left->right; - if (!gtk_tree_rbtree_is_nil (left->right)) - left->right->parent = node; - - left->parent = node->parent; - if (!gtk_tree_rbtree_is_nil (node->parent)) - { - if (node == node->parent->right) - node->parent->right = left; - else - node->parent->left = left; - } - else - { - tree->root = left; - } - - /* link node and left */ - left->right = node; - node->parent = left; - - node->count = 1 + node->left->count + node->right->count; - left->count = 1 + left->left->count + left->right->count; - - node->offset = node_height + node->left->offset + node->right->offset + - (node->children ? node->children->root->offset : 0); - left->offset = left_height + left->left->offset + left->right->offset + - (left->children ? left->children->root->offset : 0); - - fixup_validation (tree, node); - fixup_validation (tree, left); - fixup_total_count (tree, node); - fixup_total_count (tree, left); -} - -static void -gtk_tree_rbtree_insert_fixup (GtkTreeRBTree *tree, - GtkTreeRBNode *node) -{ - /* check Red-Black properties */ - while (node != tree->root && GTK_TREE_RBNODE_GET_COLOR (node->parent) == GTK_TREE_RBNODE_RED) - { - /* we have a violation */ - if (node->parent == node->parent->parent->left) - { - GtkTreeRBNode *y = node->parent->parent->right; - if (GTK_TREE_RBNODE_GET_COLOR (y) == GTK_TREE_RBNODE_RED) - { - /* uncle is GTK_TREE_RBNODE_RED */ - GTK_TREE_RBNODE_SET_COLOR (node->parent, GTK_TREE_RBNODE_BLACK); - GTK_TREE_RBNODE_SET_COLOR (y, GTK_TREE_RBNODE_BLACK); - GTK_TREE_RBNODE_SET_COLOR (node->parent->parent, GTK_TREE_RBNODE_RED); - node = node->parent->parent; - } - else - { - /* uncle is GTK_TREE_RBNODE_BLACK */ - if (node == node->parent->right) - { - /* make node a left child */ - node = node->parent; - gtk_tree_rbnode_rotate_left (tree, node); - } - - /* recolor and rotate */ - GTK_TREE_RBNODE_SET_COLOR (node->parent, GTK_TREE_RBNODE_BLACK); - GTK_TREE_RBNODE_SET_COLOR (node->parent->parent, GTK_TREE_RBNODE_RED); - gtk_tree_rbnode_rotate_right (tree, node->parent->parent); - } - } - else - { - /* mirror image of above code */ - GtkTreeRBNode *y = node->parent->parent->left; - if (GTK_TREE_RBNODE_GET_COLOR (y) == GTK_TREE_RBNODE_RED) - { - /* uncle is GTK_TREE_RBNODE_RED */ - GTK_TREE_RBNODE_SET_COLOR (node->parent, GTK_TREE_RBNODE_BLACK); - GTK_TREE_RBNODE_SET_COLOR (y, GTK_TREE_RBNODE_BLACK); - GTK_TREE_RBNODE_SET_COLOR (node->parent->parent, GTK_TREE_RBNODE_RED); - node = node->parent->parent; - } - else - { - /* uncle is GTK_TREE_RBNODE_BLACK */ - if (node == node->parent->left) - { - node = node->parent; - gtk_tree_rbnode_rotate_right (tree, node); - } - GTK_TREE_RBNODE_SET_COLOR (node->parent, GTK_TREE_RBNODE_BLACK); - GTK_TREE_RBNODE_SET_COLOR (node->parent->parent, GTK_TREE_RBNODE_RED); - gtk_tree_rbnode_rotate_left (tree, node->parent->parent); - } - } - } - GTK_TREE_RBNODE_SET_COLOR (tree->root, GTK_TREE_RBNODE_BLACK); -} - -static void -gtk_tree_rbtree_remove_node_fixup (GtkTreeRBTree *tree, - GtkTreeRBNode *node, - GtkTreeRBNode *parent) -{ - while (node != tree->root && GTK_TREE_RBNODE_GET_COLOR (node) == GTK_TREE_RBNODE_BLACK) - { - if (node == parent->left) - { - GtkTreeRBNode *w = parent->right; - if (GTK_TREE_RBNODE_GET_COLOR (w) == GTK_TREE_RBNODE_RED) - { - GTK_TREE_RBNODE_SET_COLOR (w, GTK_TREE_RBNODE_BLACK); - GTK_TREE_RBNODE_SET_COLOR (parent, GTK_TREE_RBNODE_RED); - gtk_tree_rbnode_rotate_left (tree, parent); - w = parent->right; - } - g_assert (w); - if (GTK_TREE_RBNODE_GET_COLOR (w->left) == GTK_TREE_RBNODE_BLACK && GTK_TREE_RBNODE_GET_COLOR (w->right) == GTK_TREE_RBNODE_BLACK) - { - GTK_TREE_RBNODE_SET_COLOR (w, GTK_TREE_RBNODE_RED); - node = parent; - } - else - { - if (GTK_TREE_RBNODE_GET_COLOR (w->right) == GTK_TREE_RBNODE_BLACK) - { - GTK_TREE_RBNODE_SET_COLOR (w->left, GTK_TREE_RBNODE_BLACK); - GTK_TREE_RBNODE_SET_COLOR (w, GTK_TREE_RBNODE_RED); - gtk_tree_rbnode_rotate_right (tree, w); - w = parent->right; - } - GTK_TREE_RBNODE_SET_COLOR (w, GTK_TREE_RBNODE_GET_COLOR (parent)); - GTK_TREE_RBNODE_SET_COLOR (parent, GTK_TREE_RBNODE_BLACK); - GTK_TREE_RBNODE_SET_COLOR (w->right, GTK_TREE_RBNODE_BLACK); - gtk_tree_rbnode_rotate_left (tree, parent); - node = tree->root; - } - } - else - { - GtkTreeRBNode *w = parent->left; - if (GTK_TREE_RBNODE_GET_COLOR (w) == GTK_TREE_RBNODE_RED) - { - GTK_TREE_RBNODE_SET_COLOR (w, GTK_TREE_RBNODE_BLACK); - GTK_TREE_RBNODE_SET_COLOR (parent, GTK_TREE_RBNODE_RED); - gtk_tree_rbnode_rotate_right (tree, parent); - w = parent->left; - } - g_assert (w); - if (GTK_TREE_RBNODE_GET_COLOR (w->right) == GTK_TREE_RBNODE_BLACK && GTK_TREE_RBNODE_GET_COLOR (w->left) == GTK_TREE_RBNODE_BLACK) - { - GTK_TREE_RBNODE_SET_COLOR (w, GTK_TREE_RBNODE_RED); - node = parent; - } - else - { - if (GTK_TREE_RBNODE_GET_COLOR (w->left) == GTK_TREE_RBNODE_BLACK) - { - GTK_TREE_RBNODE_SET_COLOR (w->right, GTK_TREE_RBNODE_BLACK); - GTK_TREE_RBNODE_SET_COLOR (w, GTK_TREE_RBNODE_RED); - gtk_tree_rbnode_rotate_left (tree, w); - w = parent->left; - } - GTK_TREE_RBNODE_SET_COLOR (w, GTK_TREE_RBNODE_GET_COLOR (parent)); - GTK_TREE_RBNODE_SET_COLOR (parent, GTK_TREE_RBNODE_BLACK); - GTK_TREE_RBNODE_SET_COLOR (w->left, GTK_TREE_RBNODE_BLACK); - gtk_tree_rbnode_rotate_right (tree, parent); - node = tree->root; - } - } - - parent = node->parent; - } - GTK_TREE_RBNODE_SET_COLOR (node, GTK_TREE_RBNODE_BLACK); -} - -GtkTreeRBTree * -gtk_tree_rbtree_new (void) -{ - GtkTreeRBTree *retval; - - retval = g_new (GtkTreeRBTree, 1); - retval->parent_tree = NULL; - retval->parent_node = NULL; - - retval->root = (GtkTreeRBNode *) &nil; - - return retval; -} - -static void -gtk_tree_rbtree_free_helper (GtkTreeRBTree *tree, - GtkTreeRBNode *node, - gpointer data) -{ - if (node->children) - gtk_tree_rbtree_free (node->children); - - gtk_tree_rbnode_free (node); -} - -void -gtk_tree_rbtree_free (GtkTreeRBTree *tree) -{ - gtk_tree_rbtree_traverse (tree, - tree->root, - G_POST_ORDER, - gtk_tree_rbtree_free_helper, - NULL); - - if (tree->parent_node && - tree->parent_node->children == tree) - tree->parent_node->children = NULL; - g_free (tree); -} - -static void -gtk_rbnode_adjust (GtkTreeRBTree *tree, - GtkTreeRBNode *node, - int count_diff, - int total_count_diff, - int offset_diff) -{ - while (tree && node && !gtk_tree_rbtree_is_nil (node)) - { - fixup_validation (tree, node); - node->offset += offset_diff; - node->count += count_diff; - node->total_count += total_count_diff; - - node = node->parent; - if (gtk_tree_rbtree_is_nil (node)) - { - node = tree->parent_node; - tree = tree->parent_tree; - count_diff = 0; - } - } -} - -void -gtk_tree_rbtree_remove (GtkTreeRBTree *tree) -{ -#ifdef G_ENABLE_DEBUG - GtkTreeRBTree *tmp_tree; - - if (GTK_DEBUG_CHECK (TREE)) - gtk_tree_rbtree_test (G_STRLOC, tree); -#endif - - /* ugly hack to make fixup_validation work in the first iteration of the - * loop below */ - GTK_TREE_RBNODE_UNSET_FLAG (tree->root, GTK_TREE_RBNODE_DESCENDANTS_INVALID); - - gtk_rbnode_adjust (tree->parent_tree, - tree->parent_node, - 0, - -(int) tree->root->total_count, - -tree->root->offset); - -#ifdef G_ENABLE_DEBUG - tmp_tree = tree->parent_tree; -#endif - - gtk_tree_rbtree_free (tree); - -#ifdef G_ENABLE_DEBUG - if (GTK_DEBUG_CHECK (TREE)) - gtk_tree_rbtree_test (G_STRLOC, tmp_tree); -#endif -} - - -GtkTreeRBNode * -gtk_tree_rbtree_insert_after (GtkTreeRBTree *tree, - GtkTreeRBNode *current, - int height, - gboolean valid) -{ - GtkTreeRBNode *node; - gboolean right = TRUE; - -#ifdef G_ENABLE_DEBUG - if (GTK_DEBUG_CHECK (TREE)) - { - GString *s; - - s = g_string_new (""); - g_string_append_printf (s, "gtk_tree_rbtree_insert_after: %p\n", current); - gtk_tree_rbtree_debug_spew (tree, s); - g_message ("%s", s->str); - g_string_free (s, TRUE); - gtk_tree_rbtree_test (G_STRLOC, tree); - } -#endif - - if (current != NULL && !gtk_tree_rbtree_is_nil (current->right)) - { - current = current->right; - while (!gtk_tree_rbtree_is_nil (current->left)) - current = current->left; - right = FALSE; - } - /* setup new node */ - node = gtk_tree_rbnode_new (tree, height); - - /* insert node in tree */ - if (current) - { - node->parent = current; - if (right) - current->right = node; - else - current->left = node; - gtk_rbnode_adjust (tree, node->parent, - 1, 1, height); - } - else - { - g_assert (gtk_tree_rbtree_is_nil (tree->root)); - tree->root = node; - gtk_rbnode_adjust (tree->parent_tree, tree->parent_node, - 0, 1, height); - } - - if (valid) - gtk_tree_rbtree_node_mark_valid (tree, node); - else - gtk_tree_rbtree_node_mark_invalid (tree, node); - - gtk_tree_rbtree_insert_fixup (tree, node); - -#ifdef G_ENABLE_DEBUG - if (GTK_DEBUG_CHECK (TREE)) - { - GString *s; - - s = g_string_new ("gtk_tree_rbtree_insert_after finished...\n"); - gtk_tree_rbtree_debug_spew (tree, s); - g_message ("%s", s->str); - g_string_free (s, TRUE); - gtk_tree_rbtree_test (G_STRLOC, tree); - } -#endif - - return node; -} - -GtkTreeRBNode * -gtk_tree_rbtree_insert_before (GtkTreeRBTree *tree, - GtkTreeRBNode *current, - int height, - gboolean valid) -{ - GtkTreeRBNode *node; - gboolean left = TRUE; - -#ifdef G_ENABLE_DEBUG - if (GTK_DEBUG_CHECK (TREE)) - { - GString *s; - - s = g_string_new (""); - g_string_append_printf (s, "gtk_tree_rbtree_insert_before: %p\n", current); - gtk_tree_rbtree_debug_spew (tree, s); - g_message ("%s", s->str); - g_string_free (s, TRUE); - gtk_tree_rbtree_test (G_STRLOC, tree); - } -#endif - - if (current != NULL && !gtk_tree_rbtree_is_nil (current->left)) - { - current = current->left; - while (!gtk_tree_rbtree_is_nil (current->right)) - current = current->right; - left = FALSE; - } - - /* setup new node */ - node = gtk_tree_rbnode_new (tree, height); - - /* insert node in tree */ - if (current) - { - node->parent = current; - if (left) - current->left = node; - else - current->right = node; - gtk_rbnode_adjust (tree, node->parent, - 1, 1, height); - } - else - { - g_assert (gtk_tree_rbtree_is_nil (tree->root)); - tree->root = node; - gtk_rbnode_adjust (tree->parent_tree, tree->parent_node, - 0, 1, height); - } - - if (valid) - gtk_tree_rbtree_node_mark_valid (tree, node); - else - gtk_tree_rbtree_node_mark_invalid (tree, node); - - gtk_tree_rbtree_insert_fixup (tree, node); - -#ifdef G_ENABLE_DEBUG - if (GTK_DEBUG_CHECK (TREE)) - { - GString *s; - - s = g_string_new ("gtk_tree_rbtree_insert_before finished...\n"); - gtk_tree_rbtree_debug_spew (tree, s); - g_message ("%s", s->str); - g_string_free (s, TRUE); - gtk_tree_rbtree_test (G_STRLOC, tree); - } -#endif - - return node; -} - -GtkTreeRBNode * -gtk_tree_rbtree_find_count (GtkTreeRBTree *tree, - int count) -{ - GtkTreeRBNode *node; - - node = tree->root; - while (!gtk_tree_rbtree_is_nil (node) && (node->left->count + 1 != count)) - { - if (node->left->count >= count) - node = node->left; - else - { - count -= (node->left->count + 1); - node = node->right; - } - } - if (gtk_tree_rbtree_is_nil (node)) - return NULL; - return node; -} - -void -gtk_tree_rbtree_node_set_height (GtkTreeRBTree *tree, - GtkTreeRBNode *node, - int height) -{ - int diff = height - GTK_TREE_RBNODE_GET_HEIGHT (node); - - if (diff == 0) - return; - - gtk_rbnode_adjust (tree, node, 0, 0, diff); - -#ifdef G_ENABLE_DEBUG - if (GTK_DEBUG_CHECK (TREE)) - gtk_tree_rbtree_test (G_STRLOC, tree); -#endif -} - -void -gtk_tree_rbtree_node_mark_invalid (GtkTreeRBTree *tree, - GtkTreeRBNode *node) -{ - if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_INVALID)) - return; - - GTK_TREE_RBNODE_SET_FLAG (node, GTK_TREE_RBNODE_INVALID); - do - { - if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_DESCENDANTS_INVALID)) - return; - GTK_TREE_RBNODE_SET_FLAG (node, GTK_TREE_RBNODE_DESCENDANTS_INVALID); - node = node->parent; - if (gtk_tree_rbtree_is_nil (node)) - { - node = tree->parent_node; - tree = tree->parent_tree; - } - } - while (node); -} - -#if 0 -/* Draconian version. */ -void -gtk_tree_rbtree_node_mark_invalid (GtkTreeRBTree *tree, - GtkTreeRBNode *node) -{ - GTK_TREE_RBNODE_SET_FLAG (node, GTK_TREE_RBNODE_INVALID); - do - { - fixup_validation (tree, node); - node = node->parent; - if (gtk_tree_rbtree_is_nil (node)) - { - node = tree->parent_node; - tree = tree->parent_tree; - } - } - while (node); -} -#endif - -void -gtk_tree_rbtree_node_mark_valid (GtkTreeRBTree *tree, - GtkTreeRBNode *node) -{ - if ((!GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_INVALID)) && - (!GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_COLUMN_INVALID))) - return; - - GTK_TREE_RBNODE_UNSET_FLAG (node, GTK_TREE_RBNODE_INVALID); - GTK_TREE_RBNODE_UNSET_FLAG (node, GTK_TREE_RBNODE_COLUMN_INVALID); - - do - { - if ((GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_INVALID)) || - (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_COLUMN_INVALID)) || - (node->children && GTK_TREE_RBNODE_FLAG_SET (node->children->root, GTK_TREE_RBNODE_DESCENDANTS_INVALID)) || - (GTK_TREE_RBNODE_FLAG_SET (node->left, GTK_TREE_RBNODE_DESCENDANTS_INVALID)) || - (GTK_TREE_RBNODE_FLAG_SET (node->right, GTK_TREE_RBNODE_DESCENDANTS_INVALID))) - return; - - GTK_TREE_RBNODE_UNSET_FLAG (node, GTK_TREE_RBNODE_DESCENDANTS_INVALID); - node = node->parent; - if (gtk_tree_rbtree_is_nil (node)) - { - node = tree->parent_node; - tree = tree->parent_tree; - } - } - while (node); -} - -#if 0 -/* Draconian version */ -void -gtk_tree_rbtree_node_mark_valid (GtkTreeRBTree *tree, - GtkTreeRBNode *node) -{ - GTK_TREE_RBNODE_UNSET_FLAG (node, GTK_TREE_RBNODE_INVALID); - GTK_TREE_RBNODE_UNSET_FLAG (node, GTK_TREE_RBNODE_COLUMN_INVALID); - - do - { - fixup_validation (tree, node); - node = node->parent; - if (gtk_tree_rbtree_is_nil (node)) - { - node = tree->parent_node; - tree = tree->parent_tree; - } - } - while (node); -} -#endif -/* Assume tree is the root node as it doesn't set DESCENDANTS_INVALID above. - */ -void -gtk_tree_rbtree_column_invalid (GtkTreeRBTree *tree) -{ - GtkTreeRBNode *node; - - if (tree == NULL) - return; - - for (node = gtk_tree_rbtree_first (tree); - node != NULL; - node = gtk_tree_rbtree_next (tree, node)) - { - if (!(GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_INVALID))) - GTK_TREE_RBNODE_SET_FLAG (node, GTK_TREE_RBNODE_COLUMN_INVALID); - GTK_TREE_RBNODE_SET_FLAG (node, GTK_TREE_RBNODE_DESCENDANTS_INVALID); - - if (node->children) - gtk_tree_rbtree_column_invalid (node->children); - } -} - -void -gtk_tree_rbtree_mark_invalid (GtkTreeRBTree *tree) -{ - GtkTreeRBNode *node; - - if (tree == NULL) - return; - - for (node = gtk_tree_rbtree_first (tree); - node != NULL; - node = gtk_tree_rbtree_next (tree, node)) - { - GTK_TREE_RBNODE_SET_FLAG (node, GTK_TREE_RBNODE_INVALID); - GTK_TREE_RBNODE_SET_FLAG (node, GTK_TREE_RBNODE_DESCENDANTS_INVALID); - - if (node->children) - gtk_tree_rbtree_mark_invalid (node->children); - } -} - -void -gtk_tree_rbtree_set_fixed_height (GtkTreeRBTree *tree, - int height, - gboolean mark_valid) -{ - GtkTreeRBNode *node; - - if (tree == NULL) - return; - - for (node = gtk_tree_rbtree_first (tree); - node != NULL; - node = gtk_tree_rbtree_next (tree, node)) - { - if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_INVALID)) - { - gtk_tree_rbtree_node_set_height (tree, node, height); - if (mark_valid) - gtk_tree_rbtree_node_mark_valid (tree, node); - } - - if (node->children) - gtk_tree_rbtree_set_fixed_height (node->children, height, mark_valid); - } -} - -static void -reorder_prepare (GtkTreeRBTree *tree, - GtkTreeRBNode *node, - gpointer data) -{ - node->offset -= node->left->offset + node->right->offset; - GTK_TREE_RBNODE_UNSET_FLAG (node, GTK_TREE_RBNODE_DESCENDANTS_INVALID); -} - -static void -reorder_fixup (GtkTreeRBTree *tree, - GtkTreeRBNode *node, - gpointer data) -{ - node->offset += node->left->offset + node->right->offset; - node->count = 1 + node->left->count + node->right->count; - fixup_validation (tree, node); - fixup_total_count (tree, node); -} - -static void -reorder_copy_node (GtkTreeRBTree *tree, - GtkTreeRBNode *to, - GtkTreeRBNode *from) -{ - to->flags = (to->flags & GTK_TREE_RBNODE_NON_COLORS) | GTK_TREE_RBNODE_GET_COLOR (from); - - to->left = from->left; - if (!gtk_tree_rbtree_is_nil (to->left)) - to->left->parent = to; - - to->right = from->right; - if (!gtk_tree_rbtree_is_nil (to->right)) - to->right->parent = to; - - to->parent = from->parent; - if (gtk_tree_rbtree_is_nil (to->parent)) - tree->root = to; - else if (to->parent->left == from) - to->parent->left = to; - else if (to->parent->right == from) - to->parent->right = to; -} - -/* It basically pulls everything out of the tree, rearranges it, and puts it - * back together. Our strategy is to keep the old RBTree intact, and just - * rearrange the contents. When that is done, we go through and update the - * heights. There is probably a more elegant way to write this function. If - * anyone wants to spend the time writing it, patches will be accepted. - */ -void -gtk_tree_rbtree_reorder (GtkTreeRBTree *tree, - int *new_order, - int length) -{ - GtkTreeRBNode **nodes; - GtkTreeRBNode *node; - int i, j; - - g_return_if_fail (tree != NULL); - g_return_if_fail (length > 0); - g_return_if_fail (tree->root->count == length); - - nodes = g_new (GtkTreeRBNode *, length); - - gtk_tree_rbtree_traverse (tree, tree->root, G_PRE_ORDER, reorder_prepare, NULL); - - for (node = gtk_tree_rbtree_first (tree), i = 0; - node; - node = gtk_tree_rbtree_next (tree, node), i++) - { - nodes[i] = node; - } - - for (i = 0; i < length; i++) - { - GtkTreeRBNode tmp = { 0, }; - GSList *l, *cycle = NULL; - - tmp.offset = -1; - - /* already swapped */ - if (nodes[i] == NULL) - continue; - /* no need to swap */ - if (new_order[i] == i) - continue; - - /* make a list out of the pending nodes */ - for (j = i; new_order[j] != i; j = new_order[j]) - { - cycle = g_slist_prepend (cycle, nodes[j]); - nodes[j] = NULL; - } - - node = nodes[j]; - reorder_copy_node (tree, &tmp, node); - for (l = cycle; l; l = l->next) - { - reorder_copy_node (tree, node, l->data); - node = l->data; - } - - reorder_copy_node (tree, node, &tmp); - nodes[j] = NULL; - g_slist_free (cycle); - } - - gtk_tree_rbtree_traverse (tree, tree->root, G_POST_ORDER, reorder_fixup, NULL); - - g_free (nodes); -} - -/** - * gtk_tree_rbtree_contains: - * @tree: a tree - * @potential_child: a potential child of @tree - * - * Checks if @potential_child is a child (direct or via intermediate - * trees) of @tree. - * - * Returns: %TRUE if @potential_child is a child of @tree. - **/ -gboolean -gtk_tree_rbtree_contains (GtkTreeRBTree *tree, - GtkTreeRBTree *potential_child) -{ - g_return_val_if_fail (tree != NULL, FALSE); - g_return_val_if_fail (potential_child != NULL, FALSE); - - do - { - potential_child = potential_child->parent_tree; - if (potential_child == tree) - return TRUE; - } - while (potential_child != NULL); - - return FALSE; -} - -int -gtk_tree_rbtree_node_find_offset (GtkTreeRBTree *tree, - GtkTreeRBNode *node) -{ - GtkTreeRBNode *last; - int retval; - - g_assert (node); - g_assert (node->left); - - retval = node->left->offset; - - while (tree && node && !gtk_tree_rbtree_is_nil (node)) - { - last = node; - node = node->parent; - - /* Add left branch, plus children, iff we came from the right */ - if (node->right == last) - retval += node->offset - node->right->offset; - - if (gtk_tree_rbtree_is_nil (node)) - { - node = tree->parent_node; - tree = tree->parent_tree; - - /* Add the parent node, plus the left branch. */ - if (node) - retval += node->left->offset + GTK_TREE_RBNODE_GET_HEIGHT (node); - } - } - return retval; -} - -guint -gtk_tree_rbtree_node_get_index (GtkTreeRBTree *tree, - GtkTreeRBNode *node) -{ - GtkTreeRBNode *last; - guint retval; - - g_assert (node); - g_assert (node->left); - - retval = node->left->total_count; - - while (tree && node && !gtk_tree_rbtree_is_nil (node)) - { - last = node; - node = node->parent; - - /* Add left branch, plus children, iff we came from the right */ - if (node->right == last) - retval += node->total_count - node->right->total_count; - - if (gtk_tree_rbtree_is_nil (node)) - { - node = tree->parent_node; - tree = tree->parent_tree; - - /* Add the parent node, plus the left branch. */ - if (node) - retval += node->left->total_count + 1; /* 1 == GTK_TREE_RBNODE_GET_PARITY() */ - } - } - - return retval; -} - -static int -gtk_rbtree_real_find_offset (GtkTreeRBTree *tree, - int height, - GtkTreeRBTree **new_tree, - GtkTreeRBNode **new_node) -{ - GtkTreeRBNode *tmp_node; - - g_assert (tree); - - if (height < 0) - { - *new_tree = NULL; - *new_node = NULL; - - return 0; - } - - - tmp_node = tree->root; - while (!gtk_tree_rbtree_is_nil (tmp_node) && - (tmp_node->left->offset > height || - (tmp_node->offset - tmp_node->right->offset) < height)) - { - if (tmp_node->left->offset > height) - tmp_node = tmp_node->left; - else - { - height -= (tmp_node->offset - tmp_node->right->offset); - tmp_node = tmp_node->right; - } - } - if (gtk_tree_rbtree_is_nil (tmp_node)) - { - *new_tree = NULL; - *new_node = NULL; - return 0; - } - if (tmp_node->children) - { - if ((tmp_node->offset - - tmp_node->right->offset - - tmp_node->children->root->offset) > height) - { - *new_tree = tree; - *new_node = tmp_node; - return (height - tmp_node->left->offset); - } - return gtk_rbtree_real_find_offset (tmp_node->children, - height - tmp_node->left->offset - - (tmp_node->offset - - tmp_node->left->offset - - tmp_node->right->offset - - tmp_node->children->root->offset), - new_tree, - new_node); - } - *new_tree = tree; - *new_node = tmp_node; - return (height - tmp_node->left->offset); -} - -int -gtk_tree_rbtree_find_offset (GtkTreeRBTree *tree, - int height, - GtkTreeRBTree **new_tree, - GtkTreeRBNode **new_node) -{ - g_assert (tree); - - if ((height < 0) || - (height >= tree->root->offset)) - { - *new_tree = NULL; - *new_node = NULL; - - return 0; - } - return gtk_rbtree_real_find_offset (tree, height, new_tree, new_node); -} - -gboolean -gtk_tree_rbtree_find_index (GtkTreeRBTree *tree, - guint index, - GtkTreeRBTree **new_tree, - GtkTreeRBNode **new_node) -{ - GtkTreeRBNode *tmp_node; - - g_assert (tree); - - tmp_node = tree->root; - while (!gtk_tree_rbtree_is_nil (tmp_node)) - { - if (tmp_node->left->total_count > index) - { - tmp_node = tmp_node->left; - } - else if (tmp_node->total_count - tmp_node->right->total_count <= index) - { - index -= tmp_node->total_count - tmp_node->right->total_count; - tmp_node = tmp_node->right; - } - else - { - index -= tmp_node->left->total_count; - break; - } - } - if (gtk_tree_rbtree_is_nil (tmp_node)) - { - *new_tree = NULL; - *new_node = NULL; - return FALSE; - } - - if (index > 0) - { - g_assert (tmp_node->children); - - return gtk_tree_rbtree_find_index (tmp_node->children, - index - 1, - new_tree, - new_node); - } - - *new_tree = tree; - *new_node = tmp_node; - return TRUE; -} - -void -gtk_tree_rbtree_remove_node (GtkTreeRBTree *tree, - GtkTreeRBNode *node) -{ - GtkTreeRBNode *x, *y; - int y_height; - guint y_total_count; - - g_return_if_fail (tree != NULL); - g_return_if_fail (node != NULL); - - -#ifdef G_ENABLE_DEBUG - if (GTK_DEBUG_CHECK (TREE)) - { - GString *s; - - s = g_string_new (""); - g_string_append_printf (s, "gtk_tree_rbtree_remove_node: %p\n", node); - gtk_tree_rbtree_debug_spew (tree, s); - g_message ("%s", s->str); - g_string_free (s, TRUE); - gtk_tree_rbtree_test (G_STRLOC, tree); - } -#endif - - /* make sure we're deleting a node that's actually in the tree */ - for (x = node; !gtk_tree_rbtree_is_nil (x->parent); x = x->parent) - ; - g_return_if_fail (x == tree->root); - -#ifdef G_ENABLE_DEBUG - if (GTK_DEBUG_CHECK (TREE)) - gtk_tree_rbtree_test (G_STRLOC, tree); -#endif - - if (gtk_tree_rbtree_is_nil (node->left) || - gtk_tree_rbtree_is_nil (node->right)) - { - y = node; - } - else - { - y = node->right; - - while (!gtk_tree_rbtree_is_nil (y->left)) - y = y->left; - } - - y_height = GTK_TREE_RBNODE_GET_HEIGHT (y) - + (y->children ? y->children->root->offset : 0); - y_total_count = 1 + (y->children ? y->children->root->total_count : 0); - - /* x is y's only child, or nil */ - if (!gtk_tree_rbtree_is_nil (y->left)) - x = y->left; - else - x = y->right; - - /* remove y from the parent chain */ - if (!gtk_tree_rbtree_is_nil (x)) - x->parent = y->parent; - if (!gtk_tree_rbtree_is_nil (y->parent)) - { - if (y == y->parent->left) - y->parent->left = x; - else - y->parent->right = x; - } - else - { - tree->root = x; - } - - /* We need to clean up the validity of the tree. - */ - gtk_rbnode_adjust (tree, y, -1, -y_total_count, -y_height); - - if (GTK_TREE_RBNODE_GET_COLOR (y) == GTK_TREE_RBNODE_BLACK) - gtk_tree_rbtree_remove_node_fixup (tree, x, y->parent); - - if (y != node) - { - int node_height, node_total_count; - - /* We want to see how much we remove from the aggregate values. - * This is all the children we remove plus the node's values. - */ - node_height = GTK_TREE_RBNODE_GET_HEIGHT (node) - + (node->children ? node->children->root->offset : 0); - node_total_count = 1 - + (node->children ? node->children->root->total_count : 0); - - /* Move the node over */ - if (GTK_TREE_RBNODE_GET_COLOR (node) != GTK_TREE_RBNODE_GET_COLOR (y)) - y->flags ^= (GTK_TREE_RBNODE_BLACK | GTK_TREE_RBNODE_RED); - - y->left = node->left; - if (!gtk_tree_rbtree_is_nil (y->left)) - y->left->parent = y; - y->right = node->right; - if (!gtk_tree_rbtree_is_nil (y->right)) - y->right->parent = y; - y->parent = node->parent; - if (!gtk_tree_rbtree_is_nil (y->parent)) - { - if (y->parent->left == node) - y->parent->left = y; - else - y->parent->right = y; - } - else - { - tree->root = y; - } - y->count = node->count; - y->total_count = node->total_count; - y->offset = node->offset; - - gtk_rbnode_adjust (tree, y, - 0, - y_total_count - node_total_count, - y_height - node_height); - } - - gtk_tree_rbnode_free (node); - -#ifdef G_ENABLE_DEBUG - if (GTK_DEBUG_CHECK (TREE)) - { - GString *s; - - s = g_string_new ("gtk_tree_rbtree_remove_node finished...\n"); - gtk_tree_rbtree_debug_spew (tree, s); - g_message ("%s", s->str); - g_string_free (s, TRUE); - gtk_tree_rbtree_test (G_STRLOC, tree); - } -#endif -} - -GtkTreeRBNode * -gtk_tree_rbtree_first (GtkTreeRBTree *tree) -{ - GtkTreeRBNode *node; - - node = tree->root; - - if (gtk_tree_rbtree_is_nil (node)) - return NULL; - - while (!gtk_tree_rbtree_is_nil (node->left)) - node = node->left; - - return node; -} - -GtkTreeRBNode * -gtk_tree_rbtree_next (GtkTreeRBTree *tree, - GtkTreeRBNode *node) -{ - g_return_val_if_fail (tree != NULL, NULL); - g_return_val_if_fail (node != NULL, NULL); - - /* Case 1: the node's below us. */ - if (!gtk_tree_rbtree_is_nil (node->right)) - { - node = node->right; - while (!gtk_tree_rbtree_is_nil (node->left)) - node = node->left; - return node; - } - - /* Case 2: it's an ancestor */ - while (!gtk_tree_rbtree_is_nil (node->parent)) - { - if (node->parent->right == node) - node = node->parent; - else - return (node->parent); - } - - /* Case 3: There is no next node */ - return NULL; -} - -GtkTreeRBNode * -gtk_tree_rbtree_prev (GtkTreeRBTree *tree, - GtkTreeRBNode *node) -{ - g_return_val_if_fail (tree != NULL, NULL); - g_return_val_if_fail (node != NULL, NULL); - - /* Case 1: the node's below us. */ - if (!gtk_tree_rbtree_is_nil (node->left)) - { - node = node->left; - while (!gtk_tree_rbtree_is_nil (node->right)) - node = node->right; - return node; - } - - /* Case 2: it's an ancestor */ - while (!gtk_tree_rbtree_is_nil (node->parent)) - { - if (node->parent->left == node) - node = node->parent; - else - return (node->parent); - } - - /* Case 3: There is no next node */ - return NULL; -} - -void -gtk_tree_rbtree_next_full (GtkTreeRBTree *tree, - GtkTreeRBNode *node, - GtkTreeRBTree **new_tree, - GtkTreeRBNode **new_node) -{ - g_return_if_fail (tree != NULL); - g_return_if_fail (node != NULL); - g_return_if_fail (new_tree != NULL); - g_return_if_fail (new_node != NULL); - - if (node->children) - { - *new_tree = node->children; - *new_node = (*new_tree)->root; - while (!gtk_tree_rbtree_is_nil ((*new_node)->left)) - *new_node = (*new_node)->left; - return; - } - - *new_tree = tree; - *new_node = gtk_tree_rbtree_next (tree, node); - - while ((*new_node == NULL) && - (*new_tree != NULL)) - { - *new_node = (*new_tree)->parent_node; - *new_tree = (*new_tree)->parent_tree; - if (*new_tree) - *new_node = gtk_tree_rbtree_next (*new_tree, *new_node); - } -} - -void -gtk_tree_rbtree_prev_full (GtkTreeRBTree *tree, - GtkTreeRBNode *node, - GtkTreeRBTree **new_tree, - GtkTreeRBNode **new_node) -{ - g_return_if_fail (tree != NULL); - g_return_if_fail (node != NULL); - g_return_if_fail (new_tree != NULL); - g_return_if_fail (new_node != NULL); - - *new_tree = tree; - *new_node = gtk_tree_rbtree_prev (tree, node); - - if (*new_node == NULL) - { - *new_node = (*new_tree)->parent_node; - *new_tree = (*new_tree)->parent_tree; - } - else - { - while ((*new_node)->children) - { - *new_tree = (*new_node)->children; - *new_node = (*new_tree)->root; - while (!gtk_tree_rbtree_is_nil ((*new_node)->right)) - *new_node = (*new_node)->right; - } - } -} - -int -gtk_tree_rbtree_get_depth (GtkTreeRBTree *tree) -{ - GtkTreeRBTree *tmp_tree; - int depth = 0; - - tmp_tree = tree->parent_tree; - while (tmp_tree) - { - ++depth; - tmp_tree = tmp_tree->parent_tree; - } - - return depth; -} - -static void -gtk_tree_rbtree_traverse_pre_order (GtkTreeRBTree *tree, - GtkTreeRBNode *node, - GtkTreeRBTreeTraverseFunc func, - gpointer data) -{ - if (gtk_tree_rbtree_is_nil (node)) - return; - - (*func)(tree, node, data); - gtk_tree_rbtree_traverse_pre_order (tree, node->left, func, data); - gtk_tree_rbtree_traverse_pre_order (tree, node->right, func, data); -} - -static void -gtk_tree_rbtree_traverse_post_order (GtkTreeRBTree *tree, - GtkTreeRBNode *node, - GtkTreeRBTreeTraverseFunc func, - gpointer data) -{ - if (gtk_tree_rbtree_is_nil (node)) - return; - - gtk_tree_rbtree_traverse_post_order (tree, node->left, func, data); - gtk_tree_rbtree_traverse_post_order (tree, node->right, func, data); - (*func)(tree, node, data); -} - -void -gtk_tree_rbtree_traverse (GtkTreeRBTree *tree, - GtkTreeRBNode *node, - GTraverseType order, - GtkTreeRBTreeTraverseFunc func, - gpointer data) -{ - g_return_if_fail (tree != NULL); - g_return_if_fail (node != NULL); - g_return_if_fail (func != NULL); - g_return_if_fail (order <= G_LEVEL_ORDER); - - switch (order) - { - case G_PRE_ORDER: - gtk_tree_rbtree_traverse_pre_order (tree, node, func, data); - break; - - case G_POST_ORDER: - gtk_tree_rbtree_traverse_post_order (tree, node, func, data); - break; - - case G_IN_ORDER: - case G_LEVEL_ORDER: - default: - g_warning ("unsupported traversal order."); - break; - } -} - -static inline -void fixup_validation (GtkTreeRBTree *tree, - GtkTreeRBNode *node) -{ - if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_INVALID) || - GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_COLUMN_INVALID) || - GTK_TREE_RBNODE_FLAG_SET (node->left, GTK_TREE_RBNODE_DESCENDANTS_INVALID) || - GTK_TREE_RBNODE_FLAG_SET (node->right, GTK_TREE_RBNODE_DESCENDANTS_INVALID) || - (node->children != NULL && GTK_TREE_RBNODE_FLAG_SET (node->children->root, GTK_TREE_RBNODE_DESCENDANTS_INVALID))) - { - GTK_TREE_RBNODE_SET_FLAG (node, GTK_TREE_RBNODE_DESCENDANTS_INVALID); - } - else - { - GTK_TREE_RBNODE_UNSET_FLAG (node, GTK_TREE_RBNODE_DESCENDANTS_INVALID); - } -} - -static inline -void fixup_total_count (GtkTreeRBTree *tree, - GtkTreeRBNode *node) -{ - node->total_count = 1 + - (node->children != NULL ? node->children->root->total_count : 0) + - node->left->total_count + node->right->total_count; -} - -#ifdef G_ENABLE_DEBUG -static guint -get_total_count (GtkTreeRBNode *node) -{ - guint child_total = 0; - - child_total += (guint) node->left->total_count; - child_total += (guint) node->right->total_count; - - if (node->children) - child_total += (guint) node->children->root->total_count; - - return child_total + 1; -} - -static guint -count_total (GtkTreeRBTree *tree, - GtkTreeRBNode *node) -{ - guint res; - - if (gtk_tree_rbtree_is_nil (node)) - return 0; - - res = - count_total (tree, node->left) + - count_total (tree, node->right) + - (guint) 1 + - (node->children ? count_total (node->children, node->children->root) : 0); - - if (res != node->total_count) - g_error ("total count incorrect for node"); - - if (get_total_count (node) != node->total_count) - g_error ("Node has incorrect total count %u, should be %u", node->total_count, get_total_count (node)); - - return res; -} - -static int -_count_nodes (GtkTreeRBTree *tree, - GtkTreeRBNode *node) -{ - int res; - if (gtk_tree_rbtree_is_nil (node)) - return 0; - - g_assert (node->left); - g_assert (node->right); - - res = (_count_nodes (tree, node->left) + - _count_nodes (tree, node->right) + 1); - - if (res != node->count) - g_error ("Tree failed"); - return res; -} - -static void -gtk_tree_rbtree_test_height (GtkTreeRBTree *tree, - GtkTreeRBNode *node) -{ - int computed_offset = 0; - - /* This whole test is sort of a useless truism. */ - - if (!gtk_tree_rbtree_is_nil (node->left)) - computed_offset += node->left->offset; - - if (!gtk_tree_rbtree_is_nil (node->right)) - computed_offset += node->right->offset; - - if (node->children && !gtk_tree_rbtree_is_nil (node->children->root)) - computed_offset += node->children->root->offset; - - if (GTK_TREE_RBNODE_GET_HEIGHT (node) + computed_offset != node->offset) - g_error ("node has broken offset"); - - if (!gtk_tree_rbtree_is_nil (node->left)) - gtk_tree_rbtree_test_height (tree, node->left); - - if (!gtk_tree_rbtree_is_nil (node->right)) - gtk_tree_rbtree_test_height (tree, node->right); - - if (node->children && !gtk_tree_rbtree_is_nil (node->children->root)) - gtk_tree_rbtree_test_height (node->children, node->children->root); -} - -static void -gtk_tree_rbtree_test_dirty (GtkTreeRBTree *tree, - GtkTreeRBNode *node, - int expected_dirtyness) -{ - g_assert (node); - - if (expected_dirtyness) - { - g_assert (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_COLUMN_INVALID) || - GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_INVALID) || - GTK_TREE_RBNODE_FLAG_SET (node->left, GTK_TREE_RBNODE_DESCENDANTS_INVALID) || - GTK_TREE_RBNODE_FLAG_SET (node->right, GTK_TREE_RBNODE_DESCENDANTS_INVALID) || - (node->children && GTK_TREE_RBNODE_FLAG_SET (node->children->root, GTK_TREE_RBNODE_DESCENDANTS_INVALID))); - } - else - { - g_assert (!GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_COLUMN_INVALID) && - !GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_INVALID)); - if (!gtk_tree_rbtree_is_nil (node->left)) - g_assert (!GTK_TREE_RBNODE_FLAG_SET (node->left, GTK_TREE_RBNODE_DESCENDANTS_INVALID)); - if (!gtk_tree_rbtree_is_nil (node->right)) - g_assert (!GTK_TREE_RBNODE_FLAG_SET (node->right, GTK_TREE_RBNODE_DESCENDANTS_INVALID)); - if (node->children != NULL) - g_assert (!GTK_TREE_RBNODE_FLAG_SET (node->children->root, GTK_TREE_RBNODE_DESCENDANTS_INVALID)); - } - - if (!gtk_tree_rbtree_is_nil (node->left)) - gtk_tree_rbtree_test_dirty (tree, node->left, GTK_TREE_RBNODE_FLAG_SET (node->left, GTK_TREE_RBNODE_DESCENDANTS_INVALID)); - if (!gtk_tree_rbtree_is_nil (node->right)) - gtk_tree_rbtree_test_dirty (tree, node->right, GTK_TREE_RBNODE_FLAG_SET (node->right, GTK_TREE_RBNODE_DESCENDANTS_INVALID)); - if (node->children != NULL && !gtk_tree_rbtree_is_nil (node->children->root)) - gtk_tree_rbtree_test_dirty (node->children, node->children->root, GTK_TREE_RBNODE_FLAG_SET (node->children->root, GTK_TREE_RBNODE_DESCENDANTS_INVALID)); -} - -static void gtk_tree_rbtree_test_structure (GtkTreeRBTree *tree); - -static void -gtk_tree_rbtree_test_structure_helper (GtkTreeRBTree *tree, - GtkTreeRBNode *node) -{ - g_assert (!gtk_tree_rbtree_is_nil (node)); - - g_assert (node->left != NULL); - g_assert (node->right != NULL); - g_assert (node->parent != NULL); - - if (!gtk_tree_rbtree_is_nil (node->left)) - { - g_assert (node->left->parent == node); - gtk_tree_rbtree_test_structure_helper (tree, node->left); - } - if (!gtk_tree_rbtree_is_nil (node->right)) - { - g_assert (node->right->parent == node); - gtk_tree_rbtree_test_structure_helper (tree, node->right); - } - - if (node->children != NULL) - { - g_assert (node->children->parent_tree == tree); - g_assert (node->children->parent_node == node); - - gtk_tree_rbtree_test_structure (node->children); - } -} -static void -gtk_tree_rbtree_test_structure (GtkTreeRBTree *tree) -{ - g_assert (tree->root); - if (gtk_tree_rbtree_is_nil (tree->root)) - return; - - g_assert (gtk_tree_rbtree_is_nil (tree->root->parent)); - gtk_tree_rbtree_test_structure_helper (tree, tree->root); -} - -static void -gtk_tree_rbtree_test (const char *where, - GtkTreeRBTree *tree) -{ - GtkTreeRBTree *tmp_tree; - - if (tree == NULL) - return; - - /* Test the entire tree */ - tmp_tree = tree; - while (tmp_tree->parent_tree) - tmp_tree = tmp_tree->parent_tree; - - if (gtk_tree_rbtree_is_nil (tmp_tree->root)) - return; - - gtk_tree_rbtree_test_structure (tmp_tree); - - g_assert ((_count_nodes (tmp_tree, tmp_tree->root->left) + - _count_nodes (tmp_tree, tmp_tree->root->right) + 1) == tmp_tree->root->count); - - - gtk_tree_rbtree_test_height (tmp_tree, tmp_tree->root); - gtk_tree_rbtree_test_dirty (tmp_tree, tmp_tree->root, GTK_TREE_RBNODE_FLAG_SET (tmp_tree->root, GTK_TREE_RBNODE_DESCENDANTS_INVALID)); - g_assert (count_total (tmp_tree, tmp_tree->root) == tmp_tree->root->total_count); -} - -static void -gtk_tree_rbtree_debug_spew_helper (GtkTreeRBTree *tree, - GtkTreeRBNode *node, - GString *s, - int depth) -{ - int i; - for (i = 0; i < depth; i++) - g_string_append (s, "\t"); - - g_string_append_printf (s, "(%p - %s) (Offset %d) (Parity %d) (Validity %d%d%d)\n", - node, - (GTK_TREE_RBNODE_GET_COLOR (node) == GTK_TREE_RBNODE_BLACK) ? "BLACK" : " RED ", - node->offset, - node->total_count, - (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_DESCENDANTS_INVALID)) ? 1 : 0, - (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_INVALID)) ? 1 : 0, - (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_COLUMN_INVALID)) ? 1 : 0); - if (node->children != NULL) - { - g_string_append (s, "Looking at child.\n"); - gtk_tree_rbtree_debug_spew (node->children, s); - g_string_append (s, "Done looking at child.\n"); - } - if (!gtk_tree_rbtree_is_nil (node->left)) - { - gtk_tree_rbtree_debug_spew_helper (tree, node->left, s, depth + 1); - } - if (!gtk_tree_rbtree_is_nil (node->right)) - { - gtk_tree_rbtree_debug_spew_helper (tree, node->right, s, depth + 1); - } -} - -static void -gtk_tree_rbtree_debug_spew (GtkTreeRBTree *tree, - GString *s) -{ - g_return_if_fail (tree != NULL); - - if (gtk_tree_rbtree_is_nil (tree->root)) - g_string_append (s, "Empty tree..."); - else - gtk_tree_rbtree_debug_spew_helper (tree, tree->root, s, 0); -} -#endif /* G_ENABLE_DEBUG */ diff --git a/gtk/gtktreerbtreeprivate.h b/gtk/gtktreerbtreeprivate.h deleted file mode 100644 index f998be27b6..0000000000 --- a/gtk/gtktreerbtreeprivate.h +++ /dev/null @@ -1,172 +0,0 @@ -/* gtkrbtreeprivate.h - * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -/* A Red-Black Tree implementation used specifically by GtkTreeView. - */ -#ifndef __GTK_TREE_RBTREE_PRIVATE_H__ -#define __GTK_TREE_RBTREE_PRIVATE_H__ - -#include - - -G_BEGIN_DECLS - - -typedef enum -{ - GTK_TREE_RBNODE_BLACK = 1 << 0, - GTK_TREE_RBNODE_RED = 1 << 1, - GTK_TREE_RBNODE_IS_PARENT = 1 << 2, - GTK_TREE_RBNODE_IS_SELECTED = 1 << 3, - GTK_TREE_RBNODE_IS_PRELIT = 1 << 4, - GTK_TREE_RBNODE_INVALID = 1 << 7, - GTK_TREE_RBNODE_COLUMN_INVALID = 1 << 8, - GTK_TREE_RBNODE_DESCENDANTS_INVALID = 1 << 9, - GTK_TREE_RBNODE_NON_COLORS = GTK_TREE_RBNODE_IS_PARENT | - GTK_TREE_RBNODE_IS_SELECTED | - GTK_TREE_RBNODE_IS_PRELIT | - GTK_TREE_RBNODE_INVALID | - GTK_TREE_RBNODE_COLUMN_INVALID | - GTK_TREE_RBNODE_DESCENDANTS_INVALID -} GtkTreeRBNodeColor; - -typedef struct _GtkTreeRBTree GtkTreeRBTree; -typedef struct _GtkTreeRBNode GtkTreeRBNode; -typedef struct _GtkTreeRBTreeView GtkTreeRBTreeView; - -typedef void (*GtkTreeRBTreeTraverseFunc) (GtkTreeRBTree *tree, - GtkTreeRBNode *node, - gpointer data); - -struct _GtkTreeRBTree -{ - GtkTreeRBNode *root; - GtkTreeRBTree *parent_tree; - GtkTreeRBNode *parent_node; -}; - -struct _GtkTreeRBNode -{ - guint flags : 14; - - /* count is the number of nodes beneath us, plus 1 for ourselves. - * i.e. node->left->count + node->right->count + 1 - */ - int count; - - GtkTreeRBNode *left; - GtkTreeRBNode *right; - GtkTreeRBNode *parent; - - /* count the number of total nodes beneath us, including nodes - * of children trees. - * i.e. node->left->count + node->right->count + node->children->root->count + 1 - */ - guint total_count; - - /* this is the total of sizes of - * node->left, node->right, our own height, and the height - * of all trees in ->children, iff children exists because - * the thing is expanded. - */ - int offset; - - /* Child trees */ - GtkTreeRBTree *children; -}; - - -#define GTK_TREE_RBNODE_GET_COLOR(node) (node?(((node->flags>K_TREE_RBNODE_RED)==GTK_TREE_RBNODE_RED)?GTK_TREE_RBNODE_RED:GTK_TREE_RBNODE_BLACK):GTK_TREE_RBNODE_BLACK) -#define GTK_TREE_RBNODE_SET_COLOR(node,color) if((node->flags&color)!=color)node->flags=node->flags^(GTK_TREE_RBNODE_RED|GTK_TREE_RBNODE_BLACK) -#define GTK_TREE_RBNODE_GET_HEIGHT(node) (node->offset-(node->left->offset+node->right->offset+(node->children?node->children->root->offset:0))) -#define GTK_TREE_RBNODE_SET_FLAG(node, flag) G_STMT_START{ (node->flags|=flag); }G_STMT_END -#define GTK_TREE_RBNODE_UNSET_FLAG(node, flag) G_STMT_START{ (node->flags&=~(flag)); }G_STMT_END -#define GTK_TREE_RBNODE_FLAG_SET(node, flag) (node?(((node->flags&flag)==flag)?TRUE:FALSE):FALSE) - - -GtkTreeRBTree * gtk_tree_rbtree_new (void); -void gtk_tree_rbtree_free (GtkTreeRBTree *tree); -void gtk_tree_rbtree_remove (GtkTreeRBTree *tree); -void gtk_tree_rbtree_destroy (GtkTreeRBTree *tree); -GtkTreeRBNode * gtk_tree_rbtree_insert_before (GtkTreeRBTree *tree, - GtkTreeRBNode *node, - int height, - gboolean valid); -GtkTreeRBNode * gtk_tree_rbtree_insert_after (GtkTreeRBTree *tree, - GtkTreeRBNode *node, - int height, - gboolean valid); -void gtk_tree_rbtree_remove_node (GtkTreeRBTree *tree, - GtkTreeRBNode *node); -gboolean gtk_tree_rbtree_is_nil (GtkTreeRBNode *node); -void gtk_tree_rbtree_reorder (GtkTreeRBTree *tree, - int *new_order, - int length); -gboolean gtk_tree_rbtree_contains (GtkTreeRBTree *tree, - GtkTreeRBTree *potential_child); -GtkTreeRBNode * gtk_tree_rbtree_find_count (GtkTreeRBTree *tree, - int count); -void gtk_tree_rbtree_node_set_height (GtkTreeRBTree *tree, - GtkTreeRBNode *node, - int height); -void gtk_tree_rbtree_node_mark_invalid (GtkTreeRBTree *tree, - GtkTreeRBNode *node); -void gtk_tree_rbtree_node_mark_valid (GtkTreeRBTree *tree, - GtkTreeRBNode *node); -void gtk_tree_rbtree_column_invalid (GtkTreeRBTree *tree); -void gtk_tree_rbtree_mark_invalid (GtkTreeRBTree *tree); -void gtk_tree_rbtree_set_fixed_height (GtkTreeRBTree *tree, - int height, - gboolean mark_valid); -int gtk_tree_rbtree_node_find_offset (GtkTreeRBTree *tree, - GtkTreeRBNode *node); -guint gtk_tree_rbtree_node_get_index (GtkTreeRBTree *tree, - GtkTreeRBNode *node); -gboolean gtk_tree_rbtree_find_index (GtkTreeRBTree *tree, - guint index, - GtkTreeRBTree **new_tree, - GtkTreeRBNode **new_node); -int gtk_tree_rbtree_find_offset (GtkTreeRBTree *tree, - int offset, - GtkTreeRBTree **new_tree, - GtkTreeRBNode **new_node); -void gtk_tree_rbtree_traverse (GtkTreeRBTree *tree, - GtkTreeRBNode *node, - GTraverseType order, - GtkTreeRBTreeTraverseFunc func, - gpointer data); -GtkTreeRBNode * gtk_tree_rbtree_first (GtkTreeRBTree *tree); -GtkTreeRBNode * gtk_tree_rbtree_next (GtkTreeRBTree *tree, - GtkTreeRBNode *node); -GtkTreeRBNode * gtk_tree_rbtree_prev (GtkTreeRBTree *tree, - GtkTreeRBNode *node); -void gtk_tree_rbtree_next_full (GtkTreeRBTree *tree, - GtkTreeRBNode *node, - GtkTreeRBTree **new_tree, - GtkTreeRBNode **new_node); -void gtk_tree_rbtree_prev_full (GtkTreeRBTree *tree, - GtkTreeRBNode *node, - GtkTreeRBTree **new_tree, - GtkTreeRBNode **new_node); - -int gtk_tree_rbtree_get_depth (GtkTreeRBTree *tree); - - -G_END_DECLS - - -#endif /* __GTK_TREE_RBTREE_PRIVATE_H__ */ diff --git a/gtk/gtktreeselection.c b/gtk/gtktreeselection.c deleted file mode 100644 index b03c5655c2..0000000000 --- a/gtk/gtktreeselection.c +++ /dev/null @@ -1,1567 +0,0 @@ -/* gtktreeselection.h - * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#include "config.h" -#include -#include "gtktreeselection.h" -#include "gtktreeprivate.h" -#include "gtktreerbtreeprivate.h" -#include "gtkmarshalers.h" -#include "gtkprivate.h" -#include "gtktypebuiltins.h" - - -/** - * GtkTreeSelection: - * - * The selection object for GtkTreeView - * - * The `GtkTreeSelection` object is a helper object to manage the selection - * for a `GtkTreeView` widget. The `GtkTreeSelection` object is - * automatically created when a new `GtkTreeView` widget is created, and - * cannot exist independently of this widget. The primary reason the - * `GtkTreeSelection` objects exists is for cleanliness of code and API. - * That is, there is no conceptual reason all these functions could not be - * methods on the `GtkTreeView` widget instead of a separate function. - * - * The `GtkTreeSelection` object is gotten from a `GtkTreeView` by calling - * gtk_tree_view_get_selection(). It can be manipulated to check the - * selection status of the tree, as well as select and deselect individual - * rows. Selection is done completely view side. As a result, multiple - * views of the same model can have completely different selections. - * Additionally, you cannot change the selection of a row on the model that - * is not currently displayed by the view without expanding its parents - * first. - * - * One of the important things to remember when monitoring the selection of - * a view is that the `GtkTreeSelection`::changed signal is mostly a hint. - * That is, it may only emit one signal when a range of rows is selected. - * Additionally, it may on occasion emit a `GtkTreeSelection`::changed signal - * when nothing has happened (mostly as a result of programmers calling - * select_row on an already selected row). - */ - -typedef struct _GtkTreeSelectionClass GtkTreeSelectionClass; - -struct _GtkTreeSelection -{ - GObject parent; - - GtkTreeView *tree_view; - GtkSelectionMode type; - GtkTreeSelectionFunc user_func; - gpointer user_data; - GDestroyNotify destroy; -}; - -struct _GtkTreeSelectionClass -{ - GObjectClass parent_class; - - void (* changed) (GtkTreeSelection *selection); -}; - -static void gtk_tree_selection_finalize (GObject *object); -static int gtk_tree_selection_real_select_all (GtkTreeSelection *selection); -static int gtk_tree_selection_real_unselect_all (GtkTreeSelection *selection); -static int gtk_tree_selection_real_select_node (GtkTreeSelection *selection, - GtkTreeRBTree *tree, - GtkTreeRBNode *node, - gboolean select); -static void gtk_tree_selection_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec); -static void gtk_tree_selection_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec); - -enum -{ - PROP_0, - PROP_MODE, - N_PROPERTIES -}; - -enum -{ - CHANGED, - LAST_SIGNAL -}; - -static GParamSpec *properties[N_PROPERTIES]; -static guint tree_selection_signals [LAST_SIGNAL] = { 0 }; - -G_DEFINE_TYPE(GtkTreeSelection, gtk_tree_selection, G_TYPE_OBJECT) - -static void -gtk_tree_selection_class_init (GtkTreeSelectionClass *class) -{ - GObjectClass *object_class; - - object_class = (GObjectClass*) class; - - object_class->finalize = gtk_tree_selection_finalize; - object_class->set_property = gtk_tree_selection_set_property; - object_class->get_property = gtk_tree_selection_get_property; - class->changed = NULL; - - /* Properties */ - - /** - * GtkTreeSelection:mode: - * - * Selection mode. - * See gtk_tree_selection_set_mode() for more information on this property. - */ - properties[PROP_MODE] = g_param_spec_enum ("mode", NULL, NULL, - GTK_TYPE_SELECTION_MODE, - GTK_SELECTION_SINGLE, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); - - /* Install all properties */ - g_object_class_install_properties (object_class, N_PROPERTIES, properties); - - /* Signals */ - - /** - * GtkTreeSelection::changed: - * @treeselection: the object which received the signal. - * - * Emitted whenever the selection has (possibly) changed. Please note that - * this signal is mostly a hint. It may only be emitted once when a range - * of rows are selected, and it may occasionally be emitted when nothing - * has happened. - */ - tree_selection_signals[CHANGED] = - g_signal_new (I_("changed"), - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (GtkTreeSelectionClass, changed), - NULL, NULL, - NULL, - G_TYPE_NONE, 0); -} - -static void -gtk_tree_selection_init (GtkTreeSelection *selection) -{ - selection->type = GTK_SELECTION_SINGLE; -} - -static void -gtk_tree_selection_finalize (GObject *object) -{ - GtkTreeSelection *selection = GTK_TREE_SELECTION (object); - - if (selection->destroy) - selection->destroy (selection->user_data); - - /* chain parent_class' handler */ - G_OBJECT_CLASS (gtk_tree_selection_parent_class)->finalize (object); -} - -static void -gtk_tree_selection_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - g_return_if_fail (GTK_IS_TREE_SELECTION (object)); - - switch (prop_id) - { - case PROP_MODE: - gtk_tree_selection_set_mode (GTK_TREE_SELECTION (object), g_value_get_enum (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gtk_tree_selection_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - g_return_if_fail (GTK_IS_TREE_SELECTION (object)); - - switch (prop_id) - { - case PROP_MODE: - g_value_set_enum (value, gtk_tree_selection_get_mode (GTK_TREE_SELECTION (object))); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -/** - * _gtk_tree_selection_new: - * - * Creates a new `GtkTreeSelection` object. This function should not be invoked, - * as each `GtkTreeView` will create its own `GtkTreeSelection`. - * - * Returns: A newly created `GtkTreeSelection` object. - **/ -GtkTreeSelection* -_gtk_tree_selection_new (void) -{ - GtkTreeSelection *selection; - - selection = g_object_new (GTK_TYPE_TREE_SELECTION, NULL); - - return selection; -} - -/** - * _gtk_tree_selection_new_with_tree_view: - * @tree_view: The `GtkTreeView`. - * - * Creates a new `GtkTreeSelection` object. This function should not be invoked, - * as each `GtkTreeView` will create its own `GtkTreeSelection`. - * - * Returns: A newly created `GtkTreeSelection` object. - **/ -GtkTreeSelection* -_gtk_tree_selection_new_with_tree_view (GtkTreeView *tree_view) -{ - GtkTreeSelection *selection; - - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL); - - selection = _gtk_tree_selection_new (); - _gtk_tree_selection_set_tree_view (selection, tree_view); - - return selection; -} - -/** - * _gtk_tree_selection_set_tree_view: - * @selection: A `GtkTreeSelection`. - * @tree_view: The `GtkTreeView`. - * - * Sets the `GtkTreeView` of @selection. This function should not be invoked, as - * it is used internally by `GtkTreeView`. - **/ -void -_gtk_tree_selection_set_tree_view (GtkTreeSelection *selection, - GtkTreeView *tree_view) -{ - - g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); - if (tree_view != NULL) - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - - selection->tree_view = tree_view; -} - -/** - * gtk_tree_selection_set_mode: - * @selection: A `GtkTreeSelection`. - * @type: The selection mode - * - * Sets the selection mode of the @selection. If the previous type was - * %GTK_SELECTION_MULTIPLE, then the anchor is kept selected, if it was - * previously selected. - **/ -void -gtk_tree_selection_set_mode (GtkTreeSelection *selection, - GtkSelectionMode type) -{ - GtkTreeSelectionFunc tmp_func; - - g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); - - if (selection->type == type) - return; - - if (type == GTK_SELECTION_NONE) - { - /* We do this so that we unconditionally unset all rows - */ - tmp_func = selection->user_func; - selection->user_func = NULL; - gtk_tree_selection_unselect_all (selection); - selection->user_func = tmp_func; - - _gtk_tree_view_set_anchor_path (selection->tree_view, NULL); - } - else if (type == GTK_SELECTION_SINGLE || - type == GTK_SELECTION_BROWSE) - { - GtkTreeRBTree *tree = NULL; - GtkTreeRBNode *node = NULL; - int selected = FALSE; - GtkTreePath *anchor_path = NULL; - - anchor_path = _gtk_tree_view_get_anchor_path (selection->tree_view); - - if (anchor_path) - { - _gtk_tree_view_find_node (selection->tree_view, - anchor_path, - &tree, - &node); - - if (node && GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED)) - selected = TRUE; - } - - /* We do this so that we unconditionally unset all rows - */ - tmp_func = selection->user_func; - selection->user_func = NULL; - gtk_tree_selection_unselect_all (selection); - selection->user_func = tmp_func; - - if (node && selected) - _gtk_tree_selection_internal_select_node (selection, - node, - tree, - anchor_path, - 0, - FALSE); - if (anchor_path) - gtk_tree_path_free (anchor_path); - } - - selection->type = type; - - g_object_notify_by_pspec (G_OBJECT (selection), properties[PROP_MODE]); -} - -/** - * gtk_tree_selection_get_mode: - * @selection: a `GtkTreeSelection` - * - * Gets the selection mode for @selection. See - * gtk_tree_selection_set_mode(). - * - * Returns: the current selection mode - **/ -GtkSelectionMode -gtk_tree_selection_get_mode (GtkTreeSelection *selection) -{ - g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), GTK_SELECTION_SINGLE); - - return selection->type; -} - -/** - * gtk_tree_selection_set_select_function: - * @selection: A `GtkTreeSelection`. - * @func: (nullable): The selection function. May be %NULL - * @data: The selection function’s data. May be %NULL - * @destroy: The destroy function for user data. May be %NULL - * - * Sets the selection function. - * - * If set, this function is called before any node is selected or unselected, - * giving some control over which nodes are selected. The select function - * should return %TRUE if the state of the node may be toggled, and %FALSE - * if the state of the node should be left unchanged. - */ -void -gtk_tree_selection_set_select_function (GtkTreeSelection *selection, - GtkTreeSelectionFunc func, - gpointer data, - GDestroyNotify destroy) -{ - - g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); - - if (selection->destroy) - selection->destroy (selection->user_data); - - selection->user_func = func; - selection->user_data = data; - selection->destroy = destroy; -} - -/** - * gtk_tree_selection_get_select_function: (skip) - * @selection: A `GtkTreeSelection`. - * - * Returns the current selection function. - * - * Returns: The function. - **/ -GtkTreeSelectionFunc -gtk_tree_selection_get_select_function (GtkTreeSelection *selection) -{ - g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL); - - return selection->user_func; -} - -/** - * gtk_tree_selection_get_user_data: (skip) - * @selection: A `GtkTreeSelection`. - * - * Returns the user data for the selection function. - * - * Returns: The user data. - **/ -gpointer -gtk_tree_selection_get_user_data (GtkTreeSelection *selection) -{ - g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL); - - return selection->user_data; -} - -/** - * gtk_tree_selection_get_tree_view: - * @selection: A `GtkTreeSelection` - * - * Returns the tree view associated with @selection. - * - * Returns: (transfer none): A `GtkTreeView` - **/ -GtkTreeView * -gtk_tree_selection_get_tree_view (GtkTreeSelection *selection) -{ - g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL); - - return selection->tree_view; -} - -/** - * gtk_tree_selection_get_selected: - * @selection: A `GtkTreeSelection`. - * @model: (out) (optional) (transfer none): A pointer to set to the `GtkTreeModel` - * @iter: (out) (optional): The `GtkTreeIter` - * - * Sets @iter to the currently selected node if @selection is set to - * %GTK_SELECTION_SINGLE or %GTK_SELECTION_BROWSE. @iter may be NULL if you - * just want to test if @selection has any selected nodes. @model is filled - * with the current model as a convenience. This function will not work if you - * use @selection is %GTK_SELECTION_MULTIPLE. - * - * Returns: TRUE, if there is a selected node. - **/ -gboolean -gtk_tree_selection_get_selected (GtkTreeSelection *selection, - GtkTreeModel **model, - GtkTreeIter *iter) -{ - GtkTreeRBTree *tree; - GtkTreeRBNode *node; - GtkTreePath *anchor_path; - gboolean retval = FALSE; - gboolean found_node; - - g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), FALSE); - g_return_val_if_fail (selection->type != GTK_SELECTION_MULTIPLE, FALSE); - g_return_val_if_fail (selection->tree_view != NULL, FALSE); - - /* Clear the iter */ - if (iter) - memset (iter, 0, sizeof (GtkTreeIter)); - - if (model) - *model = gtk_tree_view_get_model (selection->tree_view); - - anchor_path = _gtk_tree_view_get_anchor_path (selection->tree_view); - - if (anchor_path == NULL) - return FALSE; - - found_node = !_gtk_tree_view_find_node (selection->tree_view, - anchor_path, - &tree, - &node); - - if (found_node && GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED)) - { - /* we only want to return the anchor if it exists in the rbtree and - * is selected. - */ - if (iter == NULL) - retval = TRUE; - else - retval = gtk_tree_model_get_iter (gtk_tree_view_get_model (selection->tree_view), - iter, - anchor_path); - } - else - { - /* We don't want to return the anchor if it isn't actually selected. - */ - retval = FALSE; - } - - gtk_tree_path_free (anchor_path); - - return retval; -} - -/** - * gtk_tree_selection_get_selected_rows: - * @selection: A `GtkTreeSelection`. - * @model: (out) (optional) (transfer none): A pointer to set to the `GtkTreeModel` - * - * Creates a list of path of all selected rows. Additionally, if you are - * planning on modifying the model after calling this function, you may - * want to convert the returned list into a list of `GtkTreeRowReference`s. - * To do this, you can use gtk_tree_row_reference_new(). - * - * To free the return value, use: - * |[ - * g_list_free_full (list, (GDestroyNotify) gtk_tree_path_free); - * ]| - * - * Returns: (element-type GtkTreePath) (transfer full): A `GList` containing a `GtkTreePath` for each selected row. - **/ -GList * -gtk_tree_selection_get_selected_rows (GtkTreeSelection *selection, - GtkTreeModel **model) -{ - GList *list = NULL; - GtkTreeRBTree *tree = NULL; - GtkTreeRBNode *node = NULL; - GtkTreePath *path; - - g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL); - g_return_val_if_fail (selection->tree_view != NULL, NULL); - - if (model) - *model = gtk_tree_view_get_model (selection->tree_view); - - tree = _gtk_tree_view_get_rbtree (selection->tree_view); - - if (tree == NULL || tree->root == NULL) - return NULL; - - if (selection->type == GTK_SELECTION_NONE) - return NULL; - else if (selection->type != GTK_SELECTION_MULTIPLE) - { - GtkTreeIter iter; - - if (gtk_tree_selection_get_selected (selection, NULL, &iter)) - { - path = gtk_tree_model_get_path (gtk_tree_view_get_model (selection->tree_view), &iter); - list = g_list_append (list, path); - - return list; - } - - return NULL; - } - - node = gtk_tree_rbtree_first (tree); - path = gtk_tree_path_new_first (); - - while (node != NULL) - { - if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED)) - list = g_list_prepend (list, gtk_tree_path_copy (path)); - - if (node->children) - { - tree = node->children; - node = gtk_tree_rbtree_first (tree); - - gtk_tree_path_append_index (path, 0); - } - else - { - gboolean done = FALSE; - - do - { - node = gtk_tree_rbtree_next (tree, node); - if (node != NULL) - { - done = TRUE; - gtk_tree_path_next (path); - } - else - { - node = tree->parent_node; - tree = tree->parent_tree; - - if (!tree) - { - gtk_tree_path_free (path); - - goto done; - } - - gtk_tree_path_up (path); - } - } - while (!done); - } - } - - gtk_tree_path_free (path); - - done: - return g_list_reverse (list); -} - -static void -gtk_tree_selection_count_selected_rows_helper (GtkTreeRBTree *tree, - GtkTreeRBNode *node, - gpointer data) -{ - int *count = (int *)data; - - g_return_if_fail (node != NULL); - - if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED)) - (*count)++; - - if (node->children) - gtk_tree_rbtree_traverse (node->children, node->children->root, - G_PRE_ORDER, - gtk_tree_selection_count_selected_rows_helper, data); -} - -/** - * gtk_tree_selection_count_selected_rows: - * @selection: A `GtkTreeSelection`. - * - * Returns the number of rows that have been selected in @tree. - * - * Returns: The number of rows selected. - **/ -int -gtk_tree_selection_count_selected_rows (GtkTreeSelection *selection) -{ - int count = 0; - GtkTreeRBTree *tree; - - g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), 0); - g_return_val_if_fail (selection->tree_view != NULL, 0); - - tree = _gtk_tree_view_get_rbtree (selection->tree_view); - - if (tree == NULL || tree->root == NULL) - return 0; - - if (selection->type == GTK_SELECTION_SINGLE || - selection->type == GTK_SELECTION_BROWSE) - { - if (gtk_tree_selection_get_selected (selection, NULL, NULL)) - return 1; - else - return 0; - } - - gtk_tree_rbtree_traverse (tree, tree->root, - G_PRE_ORDER, - gtk_tree_selection_count_selected_rows_helper, - &count); - - return count; -} - -/* gtk_tree_selection_selected_foreach helper */ -static void -model_changed (gpointer data) -{ - gboolean *stop = (gboolean *)data; - - *stop = TRUE; -} - -/** - * gtk_tree_selection_selected_foreach: - * @selection: A `GtkTreeSelection`. - * @func: (scope call): The function to call for each selected node. - * @data: user data to pass to the function. - * - * Calls a function for each selected node. Note that you cannot modify - * the tree or selection from within this function. As a result, - * gtk_tree_selection_get_selected_rows() might be more useful. - **/ -void -gtk_tree_selection_selected_foreach (GtkTreeSelection *selection, - GtkTreeSelectionForeachFunc func, - gpointer data) -{ - GtkTreePath *path; - GtkTreeRBTree *tree; - GtkTreeRBNode *node; - GtkTreeIter iter; - GtkTreeModel *model; - - gulong inserted_id, deleted_id, reordered_id, changed_id; - gboolean stop = FALSE; - - g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); - g_return_if_fail (selection->tree_view != NULL); - - tree = _gtk_tree_view_get_rbtree (selection->tree_view); - - if (func == NULL || tree == NULL || tree->root == NULL) - return; - - model = gtk_tree_view_get_model (selection->tree_view); - - if (selection->type == GTK_SELECTION_SINGLE || - selection->type == GTK_SELECTION_BROWSE) - { - path = _gtk_tree_view_get_anchor_path (selection->tree_view); - - if (path) - { - gtk_tree_model_get_iter (model, &iter, path); - (* func) (model, path, &iter, data); - gtk_tree_path_free (path); - } - return; - } - - node = gtk_tree_rbtree_first (tree); - - g_object_ref (model); - - /* connect to signals to monitor changes in treemodel */ - inserted_id = g_signal_connect_swapped (model, "row-inserted", - G_CALLBACK (model_changed), - &stop); - deleted_id = g_signal_connect_swapped (model, "row-deleted", - G_CALLBACK (model_changed), - &stop); - reordered_id = g_signal_connect_swapped (model, "rows-reordered", - G_CALLBACK (model_changed), - &stop); - changed_id = g_signal_connect_swapped (selection->tree_view, "notify::model", - G_CALLBACK (model_changed), - &stop); - - /* find the node internally */ - path = gtk_tree_path_new_first (); - - while (node != NULL) - { - if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED)) - { - gtk_tree_model_get_iter (model, &iter, path); - (* func) (model, path, &iter, data); - } - - if (stop) - goto out; - - if (node->children) - { - tree = node->children; - node = gtk_tree_rbtree_first (tree); - - gtk_tree_path_append_index (path, 0); - } - else - { - gboolean done = FALSE; - - do - { - node = gtk_tree_rbtree_next (tree, node); - if (node != NULL) - { - done = TRUE; - gtk_tree_path_next (path); - } - else - { - node = tree->parent_node; - tree = tree->parent_tree; - - if (tree == NULL) - { - /* we've run out of tree */ - /* We're done with this function */ - - goto out; - } - - gtk_tree_path_up (path); - } - } - while (!done); - } - } - -out: - if (path) - gtk_tree_path_free (path); - - g_signal_handler_disconnect (model, inserted_id); - g_signal_handler_disconnect (model, deleted_id); - g_signal_handler_disconnect (model, reordered_id); - g_signal_handler_disconnect (selection->tree_view, changed_id); - g_object_unref (model); - - /* check if we have to spew a scary message */ - if (stop) - g_warning ("The model has been modified from within gtk_tree_selection_selected_foreach.\n" - "This function is for observing the selections of the tree only. If\n" - "you are trying to get all selected items from the tree, try using\n" - "gtk_tree_selection_get_selected_rows instead."); -} - -/** - * gtk_tree_selection_select_path: - * @selection: A `GtkTreeSelection`. - * @path: The `GtkTreePath` to be selected. - * - * Select the row at @path. - **/ -void -gtk_tree_selection_select_path (GtkTreeSelection *selection, - GtkTreePath *path) -{ - GtkTreeRBNode *node; - GtkTreeRBTree *tree; - gboolean ret; - GtkTreeSelectMode mode = 0; - - g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); - g_return_if_fail (selection->tree_view != NULL); - g_return_if_fail (path != NULL); - - ret = _gtk_tree_view_find_node (selection->tree_view, - path, - &tree, - &node); - - if (node == NULL || GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED) || - ret == TRUE) - return; - - if (selection->type == GTK_SELECTION_MULTIPLE) - mode = GTK_TREE_SELECT_MODE_TOGGLE; - - _gtk_tree_selection_internal_select_node (selection, - node, - tree, - path, - mode, - FALSE); -} - -/** - * gtk_tree_selection_unselect_path: - * @selection: A `GtkTreeSelection`. - * @path: The `GtkTreePath` to be unselected. - * - * Unselects the row at @path. - **/ -void -gtk_tree_selection_unselect_path (GtkTreeSelection *selection, - GtkTreePath *path) -{ - GtkTreeRBNode *node; - GtkTreeRBTree *tree; - gboolean ret; - - g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); - g_return_if_fail (selection->tree_view != NULL); - g_return_if_fail (path != NULL); - - ret = _gtk_tree_view_find_node (selection->tree_view, - path, - &tree, - &node); - - if (node == NULL || !GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED) || - ret == TRUE) - return; - - _gtk_tree_selection_internal_select_node (selection, - node, - tree, - path, - GTK_TREE_SELECT_MODE_TOGGLE, - TRUE); -} - -/** - * gtk_tree_selection_select_iter: - * @selection: A `GtkTreeSelection`. - * @iter: The `GtkTreeIter` to be selected. - * - * Selects the specified iterator. - **/ -void -gtk_tree_selection_select_iter (GtkTreeSelection *selection, - GtkTreeIter *iter) -{ - GtkTreePath *path; - GtkTreeModel *model; - - g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); - g_return_if_fail (selection->tree_view != NULL); - - model = gtk_tree_view_get_model (selection->tree_view); - g_return_if_fail (model != NULL); - g_return_if_fail (iter != NULL); - - path = gtk_tree_model_get_path (model, iter); - - if (path == NULL) - return; - - gtk_tree_selection_select_path (selection, path); - gtk_tree_path_free (path); -} - - -/** - * gtk_tree_selection_unselect_iter: - * @selection: A `GtkTreeSelection`. - * @iter: The `GtkTreeIter` to be unselected. - * - * Unselects the specified iterator. - **/ -void -gtk_tree_selection_unselect_iter (GtkTreeSelection *selection, - GtkTreeIter *iter) -{ - GtkTreePath *path; - GtkTreeModel *model; - - g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); - g_return_if_fail (selection->tree_view != NULL); - - model = gtk_tree_view_get_model (selection->tree_view); - g_return_if_fail (model != NULL); - g_return_if_fail (iter != NULL); - - path = gtk_tree_model_get_path (model, iter); - - if (path == NULL) - return; - - gtk_tree_selection_unselect_path (selection, path); - gtk_tree_path_free (path); -} - -/** - * gtk_tree_selection_path_is_selected: - * @selection: A `GtkTreeSelection`. - * @path: A `GtkTreePath` to check selection on. - * - * Returns %TRUE if the row pointed to by @path is currently selected. If @path - * does not point to a valid location, %FALSE is returned - * - * Returns: %TRUE if @path is selected. - **/ -gboolean -gtk_tree_selection_path_is_selected (GtkTreeSelection *selection, - GtkTreePath *path) -{ - GtkTreeRBNode *node; - GtkTreeRBTree *tree; - gboolean ret; - - g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), FALSE); - g_return_val_if_fail (path != NULL, FALSE); - g_return_val_if_fail (selection->tree_view != NULL, FALSE); - - if (gtk_tree_view_get_model (selection->tree_view) == NULL) - return FALSE; - - ret = _gtk_tree_view_find_node (selection->tree_view, - path, - &tree, - &node); - - if ((node == NULL) || !GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED) || - ret == TRUE) - return FALSE; - - return TRUE; -} - -/** - * gtk_tree_selection_iter_is_selected: - * @selection: A `GtkTreeSelection` - * @iter: A valid `GtkTreeIter` - * - * Returns %TRUE if the row at @iter is currently selected. - * - * Returns: %TRUE, if @iter is selected - **/ -gboolean -gtk_tree_selection_iter_is_selected (GtkTreeSelection *selection, - GtkTreeIter *iter) -{ - GtkTreePath *path; - GtkTreeModel *model; - gboolean retval; - - g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), FALSE); - g_return_val_if_fail (iter != NULL, FALSE); - g_return_val_if_fail (selection->tree_view != NULL, FALSE); - - model = gtk_tree_view_get_model (selection->tree_view); - g_return_val_if_fail (model != NULL, FALSE); - - path = gtk_tree_model_get_path (model, iter); - if (path == NULL) - return FALSE; - - retval = gtk_tree_selection_path_is_selected (selection, path); - gtk_tree_path_free (path); - - return retval; -} - - -/* Wish I was in python, right now... */ -struct _TempTuple { - GtkTreeSelection *selection; - int dirty; -}; - -static void -select_all_helper (GtkTreeRBTree *tree, - GtkTreeRBNode *node, - gpointer data) -{ - struct _TempTuple *tuple = data; - - if (node->children) - gtk_tree_rbtree_traverse (node->children, - node->children->root, - G_PRE_ORDER, - select_all_helper, - data); - if (!GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED)) - { - tuple->dirty = gtk_tree_selection_real_select_node (tuple->selection, tree, node, TRUE) || tuple->dirty; - } -} - - -/* We have a real_{un,}select_all function that doesn't emit the signal, so we - * can use it in other places without fear of the signal being emitted. - */ -static int -gtk_tree_selection_real_select_all (GtkTreeSelection *selection) -{ - struct _TempTuple *tuple; - GtkTreeRBTree *tree; - - tree = _gtk_tree_view_get_rbtree (selection->tree_view); - - if (tree == NULL) - return FALSE; - - /* Mark all nodes selected */ - tuple = g_new (struct _TempTuple, 1); - tuple->selection = selection; - tuple->dirty = FALSE; - - gtk_tree_rbtree_traverse (tree, tree->root, - G_PRE_ORDER, - select_all_helper, - tuple); - if (tuple->dirty) - { - g_free (tuple); - return TRUE; - } - g_free (tuple); - return FALSE; -} - -/** - * gtk_tree_selection_select_all: - * @selection: A `GtkTreeSelection`. - * - * Selects all the nodes. @selection must be set to %GTK_SELECTION_MULTIPLE - * mode. - **/ -void -gtk_tree_selection_select_all (GtkTreeSelection *selection) -{ - - g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); - g_return_if_fail (selection->tree_view != NULL); - - if (_gtk_tree_view_get_rbtree (selection->tree_view) == NULL || - gtk_tree_view_get_model (selection->tree_view) == NULL) - return; - - g_return_if_fail (selection->type == GTK_SELECTION_MULTIPLE); - - if (gtk_tree_selection_real_select_all (selection)) - g_signal_emit (selection, tree_selection_signals[CHANGED], 0); -} - -static void -unselect_all_helper (GtkTreeRBTree *tree, - GtkTreeRBNode *node, - gpointer data) -{ - struct _TempTuple *tuple = data; - - if (node->children) - gtk_tree_rbtree_traverse (node->children, - node->children->root, - G_PRE_ORDER, - unselect_all_helper, - data); - if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED)) - { - tuple->dirty = gtk_tree_selection_real_select_node (tuple->selection, tree, node, FALSE) || tuple->dirty; - } -} - -static gboolean -gtk_tree_selection_real_unselect_all (GtkTreeSelection *selection) -{ - struct _TempTuple *tuple; - - if (selection->type == GTK_SELECTION_SINGLE || - selection->type == GTK_SELECTION_BROWSE) - { - GtkTreeRBTree *tree = NULL; - GtkTreeRBNode *node = NULL; - GtkTreePath *anchor_path; - - anchor_path = _gtk_tree_view_get_anchor_path (selection->tree_view); - - if (anchor_path == NULL) - return FALSE; - - _gtk_tree_view_find_node (selection->tree_view, - anchor_path, - &tree, - &node); - - gtk_tree_path_free (anchor_path); - - if (tree == NULL) - return FALSE; - - if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED)) - { - if (gtk_tree_selection_real_select_node (selection, tree, node, FALSE)) - { - _gtk_tree_view_set_anchor_path (selection->tree_view, NULL); - return TRUE; - } - } - return FALSE; - } - else - { - GtkTreeRBTree *tree; - - tuple = g_new (struct _TempTuple, 1); - tuple->selection = selection; - tuple->dirty = FALSE; - - tree = _gtk_tree_view_get_rbtree (selection->tree_view); - gtk_tree_rbtree_traverse (tree, tree->root, - G_PRE_ORDER, - unselect_all_helper, - tuple); - - if (tuple->dirty) - { - g_free (tuple); - return TRUE; - } - g_free (tuple); - return FALSE; - } -} - -/** - * gtk_tree_selection_unselect_all: - * @selection: A `GtkTreeSelection`. - * - * Unselects all the nodes. - **/ -void -gtk_tree_selection_unselect_all (GtkTreeSelection *selection) -{ - - g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); - g_return_if_fail (selection->tree_view != NULL); - - if (_gtk_tree_view_get_rbtree (selection->tree_view) == NULL || - gtk_tree_view_get_model (selection->tree_view) == NULL) - return; - - if (gtk_tree_selection_real_unselect_all (selection)) - g_signal_emit (selection, tree_selection_signals[CHANGED], 0); -} - -enum -{ - RANGE_SELECT, - RANGE_UNSELECT -}; - -static int -gtk_tree_selection_real_modify_range (GtkTreeSelection *selection, - int mode, - GtkTreePath *start_path, - GtkTreePath *end_path) -{ - GtkTreeRBNode *start_node = NULL, *end_node = NULL; - GtkTreeRBTree *start_tree, *end_tree; - GtkTreePath *anchor_path = NULL; - gboolean dirty = FALSE; - - switch (gtk_tree_path_compare (start_path, end_path)) - { - case 1: - _gtk_tree_view_find_node (selection->tree_view, - end_path, - &start_tree, - &start_node); - _gtk_tree_view_find_node (selection->tree_view, - start_path, - &end_tree, - &end_node); - anchor_path = start_path; - break; - case 0: - _gtk_tree_view_find_node (selection->tree_view, - start_path, - &start_tree, - &start_node); - end_tree = start_tree; - end_node = start_node; - anchor_path = start_path; - break; - case -1: - _gtk_tree_view_find_node (selection->tree_view, - start_path, - &start_tree, - &start_node); - _gtk_tree_view_find_node (selection->tree_view, - end_path, - &end_tree, - &end_node); - anchor_path = start_path; - break; - default: - g_assert_not_reached (); - break; - } - - /* Invalid start or end node? */ - if (start_node == NULL || end_node == NULL) - return dirty; - - if (anchor_path) - _gtk_tree_view_set_anchor_path (selection->tree_view, anchor_path); - - do - { - dirty |= gtk_tree_selection_real_select_node (selection, start_tree, start_node, (mode == RANGE_SELECT)?TRUE:FALSE); - - if (start_node == end_node) - break; - - if (start_node->children) - { - start_tree = start_node->children; - start_node = gtk_tree_rbtree_first (start_tree); - } - else - { - gtk_tree_rbtree_next_full (start_tree, start_node, &start_tree, &start_node); - if (start_tree == NULL) - { - /* we just ran out of tree. That means someone passed in bogus values. - */ - return dirty; - } - } - } - while (TRUE); - - return dirty; -} - -/** - * gtk_tree_selection_select_range: - * @selection: A `GtkTreeSelection`. - * @start_path: The initial node of the range. - * @end_path: The final node of the range. - * - * Selects a range of nodes, determined by @start_path and @end_path inclusive. - * @selection must be set to %GTK_SELECTION_MULTIPLE mode. - **/ -void -gtk_tree_selection_select_range (GtkTreeSelection *selection, - GtkTreePath *start_path, - GtkTreePath *end_path) -{ - - g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); - g_return_if_fail (selection->tree_view != NULL); - g_return_if_fail (selection->type == GTK_SELECTION_MULTIPLE); - g_return_if_fail (gtk_tree_view_get_model (selection->tree_view) != NULL); - - if (gtk_tree_selection_real_modify_range (selection, RANGE_SELECT, start_path, end_path)) - g_signal_emit (selection, tree_selection_signals[CHANGED], 0); -} - -/** - * gtk_tree_selection_unselect_range: - * @selection: A `GtkTreeSelection`. - * @start_path: The initial node of the range. - * @end_path: The initial node of the range. - * - * Unselects a range of nodes, determined by @start_path and @end_path - * inclusive. - **/ -void -gtk_tree_selection_unselect_range (GtkTreeSelection *selection, - GtkTreePath *start_path, - GtkTreePath *end_path) -{ - - g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); - g_return_if_fail (selection->tree_view != NULL); - g_return_if_fail (gtk_tree_view_get_model (selection->tree_view) != NULL); - - if (gtk_tree_selection_real_modify_range (selection, RANGE_UNSELECT, start_path, end_path)) - g_signal_emit (selection, tree_selection_signals[CHANGED], 0); -} - -gboolean -_gtk_tree_selection_row_is_selectable (GtkTreeSelection *selection, - GtkTreeRBNode *node, - GtkTreePath *path) -{ - GtkTreeIter iter; - GtkTreeModel *model; - GtkTreeViewRowSeparatorFunc separator_func; - gpointer separator_data; - gboolean sensitive = FALSE; - - model = gtk_tree_view_get_model (selection->tree_view); - - _gtk_tree_view_get_row_separator_func (selection->tree_view, - &separator_func, &separator_data); - - if (!gtk_tree_model_get_iter (model, &iter, path)) - sensitive = TRUE; - - if (!sensitive && separator_func) - { - /* never allow separators to be selected */ - if ((* separator_func) (model, &iter, separator_data)) - return FALSE; - } - - if (selection->user_func) - return (*selection->user_func) (selection, model, path, - GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED), - selection->user_data); - else - return TRUE; -} - - -/* Called internally by gtktreeview.c It handles actually selecting the tree. - */ - -/* - * docs about the “override_browse_mode”, we set this flag when we want to - * unset select the node and override the select browse mode behaviour (that is - * 'one node should *always* be selected'). - */ -void -_gtk_tree_selection_internal_select_node (GtkTreeSelection *selection, - GtkTreeRBNode *node, - GtkTreeRBTree *tree, - GtkTreePath *path, - GtkTreeSelectMode mode, - gboolean override_browse_mode) -{ - int flags; - int dirty = FALSE; - GtkTreePath *anchor_path = NULL; - - if (selection->type == GTK_SELECTION_NONE) - return; - - anchor_path = _gtk_tree_view_get_anchor_path (selection->tree_view); - - if (selection->type == GTK_SELECTION_SINGLE || - selection->type == GTK_SELECTION_BROWSE) - { - /* just unselect */ - if (selection->type == GTK_SELECTION_BROWSE && override_browse_mode) - { - dirty = gtk_tree_selection_real_unselect_all (selection); - } - /* Did we try to select the same node again? */ - else if (selection->type == GTK_SELECTION_SINGLE && - anchor_path && gtk_tree_path_compare (path, anchor_path) == 0) - { - if ((mode & GTK_TREE_SELECT_MODE_TOGGLE) == GTK_TREE_SELECT_MODE_TOGGLE) - { - dirty = gtk_tree_selection_real_unselect_all (selection); - } - } - else - { - if (anchor_path) - { - /* We only want to select the new node if we can unselect the old one, - * and we can select the new one. */ - dirty = _gtk_tree_selection_row_is_selectable (selection, node, path); - - /* if dirty is FALSE, we weren't able to select the new one, otherwise, we try to - * unselect the new one - */ - if (dirty) - dirty = gtk_tree_selection_real_unselect_all (selection); - - /* if dirty is TRUE at this point, we successfully unselected the - * old one, and can then select the new one */ - if (dirty) - { - - _gtk_tree_view_set_anchor_path (selection->tree_view, NULL); - - if (gtk_tree_selection_real_select_node (selection, tree, node, TRUE)) - _gtk_tree_view_set_anchor_path (selection->tree_view, path); - } - } - else - { - if (gtk_tree_selection_real_select_node (selection, tree, node, TRUE)) - { - dirty = TRUE; - - _gtk_tree_view_set_anchor_path (selection->tree_view, path); - } - } - } - } - else if (selection->type == GTK_SELECTION_MULTIPLE) - { - if ((mode & GTK_TREE_SELECT_MODE_EXTEND) == GTK_TREE_SELECT_MODE_EXTEND - && (anchor_path == NULL)) - { - _gtk_tree_view_set_anchor_path (selection->tree_view, path); - - dirty = gtk_tree_selection_real_select_node (selection, tree, node, TRUE); - } - else if ((mode & (GTK_TREE_SELECT_MODE_EXTEND | GTK_TREE_SELECT_MODE_TOGGLE)) == (GTK_TREE_SELECT_MODE_EXTEND | GTK_TREE_SELECT_MODE_TOGGLE)) - { - gtk_tree_selection_select_range (selection, - anchor_path, - path); - } - else if ((mode & GTK_TREE_SELECT_MODE_TOGGLE) == GTK_TREE_SELECT_MODE_TOGGLE) - { - flags = node->flags; - - _gtk_tree_view_set_anchor_path (selection->tree_view, path); - - if ((flags & GTK_TREE_RBNODE_IS_SELECTED) == GTK_TREE_RBNODE_IS_SELECTED) - dirty |= gtk_tree_selection_real_select_node (selection, tree, node, FALSE); - else - dirty |= gtk_tree_selection_real_select_node (selection, tree, node, TRUE); - } - else if ((mode & GTK_TREE_SELECT_MODE_EXTEND) == GTK_TREE_SELECT_MODE_EXTEND) - { - dirty = gtk_tree_selection_real_unselect_all (selection); - dirty |= gtk_tree_selection_real_modify_range (selection, - RANGE_SELECT, - anchor_path, - path); - } - else - { - dirty = gtk_tree_selection_real_unselect_all (selection); - - _gtk_tree_view_set_anchor_path (selection->tree_view, path); - - dirty |= gtk_tree_selection_real_select_node (selection, tree, node, TRUE); - } - } - - if (anchor_path) - gtk_tree_path_free (anchor_path); - - if (dirty) - g_signal_emit (selection, tree_selection_signals[CHANGED], 0); -} - - -void -_gtk_tree_selection_emit_changed (GtkTreeSelection *selection) -{ - g_signal_emit (selection, tree_selection_signals[CHANGED], 0); -} - -/* NOTE: Any {un,}selection ever done _MUST_ be done through this function! - */ - -static int -gtk_tree_selection_real_select_node (GtkTreeSelection *selection, - GtkTreeRBTree *tree, - GtkTreeRBNode *node, - gboolean select) -{ - gboolean toggle = FALSE; - GtkTreePath *path = NULL; - - g_return_val_if_fail (node != NULL, FALSE); - - select = !! select; - - if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED) != select) - { - path = _gtk_tree_path_new_from_rbtree (tree, node); - toggle = _gtk_tree_selection_row_is_selectable (selection, node, path); - gtk_tree_path_free (path); - } - - if (toggle) - { - if (!GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED)) - { - GTK_TREE_RBNODE_SET_FLAG (node, GTK_TREE_RBNODE_IS_SELECTED); - } - else - { - GTK_TREE_RBNODE_UNSET_FLAG (node, GTK_TREE_RBNODE_IS_SELECTED); - } - - gtk_widget_queue_draw (GTK_WIDGET (selection->tree_view)); - - return TRUE; - } - - return FALSE; -} diff --git a/gtk/gtktreeselection.h b/gtk/gtktreeselection.h deleted file mode 100644 index 8eb5565231..0000000000 --- a/gtk/gtktreeselection.h +++ /dev/null @@ -1,144 +0,0 @@ -/* gtktreeselection.h - * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#ifndef __GTK_TREE_SELECTION_H__ -#define __GTK_TREE_SELECTION_H__ - -#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) -#error "Only can be included directly." -#endif - -#include - -G_BEGIN_DECLS - - -#define GTK_TYPE_TREE_SELECTION (gtk_tree_selection_get_type ()) -#define GTK_TREE_SELECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_TREE_SELECTION, GtkTreeSelection)) -#define GTK_IS_TREE_SELECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_TREE_SELECTION)) - -/** - * GtkTreeSelectionFunc: - * @selection: A `GtkTreeSelection` - * @model: A `GtkTreeModel` being viewed - * @path: The `GtkTreePath` of the row in question - * @path_currently_selected: %TRUE, if the path is currently selected - * @data: (closure): user data - * - * A function used by gtk_tree_selection_set_select_function() to filter - * whether or not a row may be selected. It is called whenever a row's - * state might change. - * - * A return value of %TRUE indicates to @selection that it is okay to - * change the selection. - * - * Returns: %TRUE, if the selection state of the row can be toggled - */ -typedef gboolean (* GtkTreeSelectionFunc) (GtkTreeSelection *selection, - GtkTreeModel *model, - GtkTreePath *path, - gboolean path_currently_selected, - gpointer data); - -/** - * GtkTreeSelectionForeachFunc: - * @model: The `GtkTreeModel` being viewed - * @path: The `GtkTreePath` of a selected row - * @iter: A `GtkTreeIter` pointing to a selected row - * @data: (closure): user data - * - * A function used by gtk_tree_selection_selected_foreach() to map all - * selected rows. It will be called on every selected row in the view. - */ -typedef void (* GtkTreeSelectionForeachFunc) (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data); - - -GDK_AVAILABLE_IN_ALL -GType gtk_tree_selection_get_type (void) G_GNUC_CONST; - -GDK_AVAILABLE_IN_ALL -void gtk_tree_selection_set_mode (GtkTreeSelection *selection, - GtkSelectionMode type); -GDK_AVAILABLE_IN_ALL -GtkSelectionMode gtk_tree_selection_get_mode (GtkTreeSelection *selection); -GDK_AVAILABLE_IN_ALL -void gtk_tree_selection_set_select_function (GtkTreeSelection *selection, - GtkTreeSelectionFunc func, - gpointer data, - GDestroyNotify destroy); -GDK_AVAILABLE_IN_ALL -gpointer gtk_tree_selection_get_user_data (GtkTreeSelection *selection); -GDK_AVAILABLE_IN_ALL -GtkTreeView* gtk_tree_selection_get_tree_view (GtkTreeSelection *selection); - -GDK_AVAILABLE_IN_ALL -GtkTreeSelectionFunc gtk_tree_selection_get_select_function (GtkTreeSelection *selection); - -/* Only meaningful if GTK_SELECTION_SINGLE or GTK_SELECTION_BROWSE is set */ -/* Use selected_foreach or get_selected_rows for GTK_SELECTION_MULTIPLE */ -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_selection_get_selected (GtkTreeSelection *selection, - GtkTreeModel **model, - GtkTreeIter *iter); -GDK_AVAILABLE_IN_ALL -GList * gtk_tree_selection_get_selected_rows (GtkTreeSelection *selection, - GtkTreeModel **model); -GDK_AVAILABLE_IN_ALL -int gtk_tree_selection_count_selected_rows (GtkTreeSelection *selection); -GDK_AVAILABLE_IN_ALL -void gtk_tree_selection_selected_foreach (GtkTreeSelection *selection, - GtkTreeSelectionForeachFunc func, - gpointer data); -GDK_AVAILABLE_IN_ALL -void gtk_tree_selection_select_path (GtkTreeSelection *selection, - GtkTreePath *path); -GDK_AVAILABLE_IN_ALL -void gtk_tree_selection_unselect_path (GtkTreeSelection *selection, - GtkTreePath *path); -GDK_AVAILABLE_IN_ALL -void gtk_tree_selection_select_iter (GtkTreeSelection *selection, - GtkTreeIter *iter); -GDK_AVAILABLE_IN_ALL -void gtk_tree_selection_unselect_iter (GtkTreeSelection *selection, - GtkTreeIter *iter); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_selection_path_is_selected (GtkTreeSelection *selection, - GtkTreePath *path); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_selection_iter_is_selected (GtkTreeSelection *selection, - GtkTreeIter *iter); -GDK_AVAILABLE_IN_ALL -void gtk_tree_selection_select_all (GtkTreeSelection *selection); -GDK_AVAILABLE_IN_ALL -void gtk_tree_selection_unselect_all (GtkTreeSelection *selection); -GDK_AVAILABLE_IN_ALL -void gtk_tree_selection_select_range (GtkTreeSelection *selection, - GtkTreePath *start_path, - GtkTreePath *end_path); -GDK_AVAILABLE_IN_ALL -void gtk_tree_selection_unselect_range (GtkTreeSelection *selection, - GtkTreePath *start_path, - GtkTreePath *end_path); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkTreeSelection, g_object_unref) - -G_END_DECLS - -#endif /* __GTK_TREE_SELECTION_H__ */ diff --git a/gtk/gtktreesortable.c b/gtk/gtktreesortable.c deleted file mode 100644 index d234530d8c..0000000000 --- a/gtk/gtktreesortable.c +++ /dev/null @@ -1,263 +0,0 @@ -/* gtktreesortable.c - * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - - -#include "config.h" -#include "gtktreesortable.h" -#include "gtkmarshalers.h" -#include "gtkprivate.h" - - -/** - * GtkTreeSortable: - * - * The interface for sortable models used by GtkTreeView - * - * `GtkTreeSortable` is an interface to be implemented by tree models which - * support sorting. The `GtkTreeView` uses the methods provided by this interface - * to sort the model. - */ - - -static void gtk_tree_sortable_base_init (gpointer g_class); - -GType -gtk_tree_sortable_get_type (void) -{ - static GType tree_sortable_type = 0; - - if (! tree_sortable_type) - { - const GTypeInfo tree_sortable_info = - { - sizeof (GtkTreeSortableIface), /* class_size */ - gtk_tree_sortable_base_init, /* base_init */ - NULL, /* base_finalize */ - NULL, - NULL, /* class_finalize */ - NULL, /* class_data */ - 0, - 0, - NULL - }; - - tree_sortable_type = - g_type_register_static (G_TYPE_INTERFACE, I_("GtkTreeSortable"), - &tree_sortable_info, 0); - - g_type_interface_add_prerequisite (tree_sortable_type, GTK_TYPE_TREE_MODEL); - } - - return tree_sortable_type; -} - -static void -gtk_tree_sortable_base_init (gpointer g_class) -{ - static gboolean initialized = FALSE; - - if (! initialized) - { - /** - * GtkTreeSortable::sort-column-changed: - * @sortable: the object on which the signal is emitted - * - * The ::sort-column-changed signal is emitted when the sort column - * or sort order of @sortable is changed. The signal is emitted before - * the contents of @sortable are resorted. - */ - g_signal_new (I_("sort-column-changed"), - GTK_TYPE_TREE_SORTABLE, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkTreeSortableIface, sort_column_changed), - NULL, NULL, - NULL, - G_TYPE_NONE, 0); - initialized = TRUE; - } -} - -/** - * gtk_tree_sortable_sort_column_changed: - * @sortable: A `GtkTreeSortable` - * - * Emits a `GtkTreeSortable::sort-column-changed` signal on @sortable. - */ -void -gtk_tree_sortable_sort_column_changed (GtkTreeSortable *sortable) -{ - g_return_if_fail (GTK_IS_TREE_SORTABLE (sortable)); - - g_signal_emit_by_name (sortable, "sort-column-changed"); -} - -/** - * gtk_tree_sortable_get_sort_column_id: - * @sortable: A `GtkTreeSortable` - * @sort_column_id: (out): The sort column id to be filled in - * @order: (out): The `GtkSortType` to be filled in - * - * Fills in @sort_column_id and @order with the current sort column and the - * order. It returns %TRUE unless the @sort_column_id is - * %GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID or - * %GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID. - * - * Returns: %TRUE if the sort column is not one of the special sort - * column ids. - **/ -gboolean -gtk_tree_sortable_get_sort_column_id (GtkTreeSortable *sortable, - int *sort_column_id, - GtkSortType *order) -{ - GtkTreeSortableIface *iface; - - g_return_val_if_fail (GTK_IS_TREE_SORTABLE (sortable), FALSE); - - iface = GTK_TREE_SORTABLE_GET_IFACE (sortable); - - g_return_val_if_fail (iface != NULL, FALSE); - g_return_val_if_fail (iface->get_sort_column_id != NULL, FALSE); - - return (* iface->get_sort_column_id) (sortable, sort_column_id, order); -} - -/** - * gtk_tree_sortable_set_sort_column_id: - * @sortable: A `GtkTreeSortable` - * @sort_column_id: the sort column id to set - * @order: The sort order of the column - * - * Sets the current sort column to be @sort_column_id. The @sortable will - * resort itself to reflect this change, after emitting a - * `GtkTreeSortable::sort-column-changed` signal. @sort_column_id may either be - * a regular column id, or one of the following special values: - * - * - %GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID: the default sort function - * will be used, if it is set - * - * - %GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID: no sorting will occur - */ -void -gtk_tree_sortable_set_sort_column_id (GtkTreeSortable *sortable, - int sort_column_id, - GtkSortType order) -{ - GtkTreeSortableIface *iface; - - g_return_if_fail (GTK_IS_TREE_SORTABLE (sortable)); - - iface = GTK_TREE_SORTABLE_GET_IFACE (sortable); - - g_return_if_fail (iface != NULL); - g_return_if_fail (iface->set_sort_column_id != NULL); - - (* iface->set_sort_column_id) (sortable, sort_column_id, order); -} - -/** - * gtk_tree_sortable_set_sort_func: - * @sortable: A `GtkTreeSortable` - * @sort_column_id: the sort column id to set the function for - * @sort_func: The comparison function - * @user_data: (closure): User data to pass to @sort_func - * @destroy: (nullable): Destroy notifier of @user_data - * - * Sets the comparison function used when sorting to be @sort_func. If the - * current sort column id of @sortable is the same as @sort_column_id, then - * the model will sort using this function. - */ -void -gtk_tree_sortable_set_sort_func (GtkTreeSortable *sortable, - int sort_column_id, - GtkTreeIterCompareFunc sort_func, - gpointer user_data, - GDestroyNotify destroy) -{ - GtkTreeSortableIface *iface; - - g_return_if_fail (GTK_IS_TREE_SORTABLE (sortable)); - g_return_if_fail (sort_func != NULL); - - iface = GTK_TREE_SORTABLE_GET_IFACE (sortable); - - g_return_if_fail (iface != NULL); - g_return_if_fail (iface->set_sort_func != NULL); - g_return_if_fail (sort_column_id >= 0); - - (* iface->set_sort_func) (sortable, sort_column_id, sort_func, user_data, destroy); -} - -/** - * gtk_tree_sortable_set_default_sort_func: - * @sortable: A `GtkTreeSortable` - * @sort_func: The comparison function - * @user_data: (closure): User data to pass to @sort_func - * @destroy: (nullable): Destroy notifier of @user_data - * - * Sets the default comparison function used when sorting to be @sort_func. - * If the current sort column id of @sortable is - * %GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, then the model will sort using - * this function. - * - * If @sort_func is %NULL, then there will be no default comparison function. - * This means that once the model has been sorted, it can’t go back to the - * default state. In this case, when the current sort column id of @sortable - * is %GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, the model will be unsorted. - */ -void -gtk_tree_sortable_set_default_sort_func (GtkTreeSortable *sortable, - GtkTreeIterCompareFunc sort_func, - gpointer user_data, - GDestroyNotify destroy) -{ - GtkTreeSortableIface *iface; - - g_return_if_fail (GTK_IS_TREE_SORTABLE (sortable)); - - iface = GTK_TREE_SORTABLE_GET_IFACE (sortable); - - g_return_if_fail (iface != NULL); - g_return_if_fail (iface->set_default_sort_func != NULL); - - (* iface->set_default_sort_func) (sortable, sort_func, user_data, destroy); -} - -/** - * gtk_tree_sortable_has_default_sort_func: - * @sortable: A `GtkTreeSortable` - * - * Returns %TRUE if the model has a default sort function. This is used - * primarily by GtkTreeViewColumns in order to determine if a model can - * go back to the default state, or not. - * - * Returns: %TRUE, if the model has a default sort function - */ -gboolean -gtk_tree_sortable_has_default_sort_func (GtkTreeSortable *sortable) -{ - GtkTreeSortableIface *iface; - - g_return_val_if_fail (GTK_IS_TREE_SORTABLE (sortable), FALSE); - - iface = GTK_TREE_SORTABLE_GET_IFACE (sortable); - - g_return_val_if_fail (iface != NULL, FALSE); - g_return_val_if_fail (iface->has_default_sort_func != NULL, FALSE); - - return (* iface->has_default_sort_func) (sortable); -} diff --git a/gtk/gtktreesortable.h b/gtk/gtktreesortable.h deleted file mode 100644 index 0cd770f8c5..0000000000 --- a/gtk/gtktreesortable.h +++ /dev/null @@ -1,164 +0,0 @@ -/* gtktreesortable.h - * Copyright (C) 2001 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#ifndef __GTK_TREE_SORTABLE_H__ -#define __GTK_TREE_SORTABLE_H__ - - -#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) -#error "Only can be included directly." -#endif - -#include -#include - - -G_BEGIN_DECLS - -#define GTK_TYPE_TREE_SORTABLE (gtk_tree_sortable_get_type ()) -#define GTK_TREE_SORTABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_TREE_SORTABLE, GtkTreeSortable)) -#define GTK_IS_TREE_SORTABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_TREE_SORTABLE)) -#define GTK_TREE_SORTABLE_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GTK_TYPE_TREE_SORTABLE, GtkTreeSortableIface)) - -/** - * GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID: - * - * Uses the default sort function in a [iface@Gtk.TreeSortable]. - * - * See also: [method@Gtk.TreeSortable.set_sort_column_id] - */ -#define GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID (-1) - -/** - * GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID: - * - * Disables sorting in a [iface@Gtk.TreeSortable]. - * - * See also: [method@Gtk.TreeSortable.set_sort_column_id] - */ -#define GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID (-2) - -typedef struct _GtkTreeSortable GtkTreeSortable; /* Dummy typedef */ -typedef struct _GtkTreeSortableIface GtkTreeSortableIface; - -/** - * GtkTreeIterCompareFunc: - * @model: The `GtkTreeModel` the comparison is within - * @a: A `GtkTreeIter` in @model - * @b: Another `GtkTreeIter` in @model - * @user_data: Data passed when the compare func is assigned e.g. by - * gtk_tree_sortable_set_sort_func() - * - * A GtkTreeIterCompareFunc should return a negative integer, zero, or a positive - * integer if @a sorts before @b, @a sorts with @b, or @a sorts after @b - * respectively. - * - * If two iters compare as equal, their order in the sorted model - * is undefined. In order to ensure that the `GtkTreeSortable` behaves as - * expected, the GtkTreeIterCompareFunc must define a partial order on - * the model, i.e. it must be reflexive, antisymmetric and transitive. - * - * For example, if @model is a product catalogue, then a compare function - * for the “price” column could be one which returns - * `price_of(@a) - price_of(@b)`. - * - * Returns: a negative integer, zero or a positive integer depending on whether - * @a sorts before, with or after @b - */ -typedef int (* GtkTreeIterCompareFunc) (GtkTreeModel *model, - GtkTreeIter *a, - GtkTreeIter *b, - gpointer user_data); - - -/** - * GtkTreeSortableIface: - * @sort_column_changed: Signal emitted when the sort column or sort - * order of sortable is changed. - * @get_sort_column_id: Fills in sort_column_id and order with the - * current sort column and the order. - * @set_sort_column_id: Sets the current sort column to be - * sort_column_id. - * @set_sort_func: Sets the comparison function used when sorting to - * be sort_func. - * @set_default_sort_func: Sets the default comparison function used - * when sorting to be sort_func. - * @has_default_sort_func: %TRUE if the model has a default sort - * function. - */ -struct _GtkTreeSortableIface -{ - /*< private >*/ - GTypeInterface g_iface; - - /*< public >*/ - - /* signals */ - void (* sort_column_changed) (GtkTreeSortable *sortable); - - /* virtual table */ - gboolean (* get_sort_column_id) (GtkTreeSortable *sortable, - int *sort_column_id, - GtkSortType *order); - void (* set_sort_column_id) (GtkTreeSortable *sortable, - int sort_column_id, - GtkSortType order); - void (* set_sort_func) (GtkTreeSortable *sortable, - int sort_column_id, - GtkTreeIterCompareFunc sort_func, - gpointer user_data, - GDestroyNotify destroy); - void (* set_default_sort_func) (GtkTreeSortable *sortable, - GtkTreeIterCompareFunc sort_func, - gpointer user_data, - GDestroyNotify destroy); - gboolean (* has_default_sort_func) (GtkTreeSortable *sortable); -}; - - -GDK_AVAILABLE_IN_ALL -GType gtk_tree_sortable_get_type (void) G_GNUC_CONST; - -GDK_AVAILABLE_IN_ALL -void gtk_tree_sortable_sort_column_changed (GtkTreeSortable *sortable); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_sortable_get_sort_column_id (GtkTreeSortable *sortable, - int *sort_column_id, - GtkSortType *order); -GDK_AVAILABLE_IN_ALL -void gtk_tree_sortable_set_sort_column_id (GtkTreeSortable *sortable, - int sort_column_id, - GtkSortType order); -GDK_AVAILABLE_IN_ALL -void gtk_tree_sortable_set_sort_func (GtkTreeSortable *sortable, - int sort_column_id, - GtkTreeIterCompareFunc sort_func, - gpointer user_data, - GDestroyNotify destroy); -GDK_AVAILABLE_IN_ALL -void gtk_tree_sortable_set_default_sort_func (GtkTreeSortable *sortable, - GtkTreeIterCompareFunc sort_func, - gpointer user_data, - GDestroyNotify destroy); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_sortable_has_default_sort_func (GtkTreeSortable *sortable); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkTreeSortable, g_object_unref) - -G_END_DECLS - -#endif /* __GTK_TREE_SORTABLE_H__ */ diff --git a/gtk/gtktreestore.c b/gtk/gtktreestore.c deleted file mode 100644 index 308f1c2853..0000000000 --- a/gtk/gtktreestore.c +++ /dev/null @@ -1,3698 +0,0 @@ -/* gtktreestore.c - * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#include "config.h" -#include -#include -#include "gtktreemodel.h" -#include "gtktreestore.h" -#include "gtktreedatalistprivate.h" -#include "gtktreednd.h" -#include "gtkbuildable.h" -#include "gtkbuilderprivate.h" -#include "gtkdebug.h" - - -/** - * GtkTreeStore: - * - * A tree-like data structure that can be used with the GtkTreeView - * - * The `GtkTreeStore` object is a list model for use with a `GtkTreeView` - * widget. It implements the `GtkTreeModel` interface, and consequently, - * can use all of the methods available there. It also implements the - * `GtkTreeSortable` interface so it can be sorted by the view. Finally, - * it also implements the tree - * [drag and drop][gtk3-GtkTreeView-drag-and-drop] - * interfaces. - * - * # GtkTreeStore as GtkBuildable - * - * The GtkTreeStore implementation of the `GtkBuildable` interface allows - * to specify the model columns with a element that may contain - * multiple elements, each specifying one model column. The “type” - * attribute specifies the data type for the column. - * - * An example of a UI Definition fragment for a tree store: - * |[ - * - * - * - * - * - * - * - * ]| - */ - -struct _GtkTreeStorePrivate -{ - int stamp; - GtkSortType order; - gpointer root; - gpointer last; - int n_columns; - int sort_column_id; - GList *sort_list; - GType *column_headers; - GtkTreeIterCompareFunc default_sort_func; - gpointer default_sort_data; - GDestroyNotify default_sort_destroy; - guint columns_dirty : 1; -}; - - -#define G_NODE(node) ((GNode *)node) -#define GTK_TREE_STORE_IS_SORTED(tree) (((GtkTreeStore*)(tree))->priv->sort_column_id != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID) -#define VALID_ITER(iter, tree_store) ((iter)!= NULL && (iter)->user_data != NULL && ((GtkTreeStore*)(tree_store))->priv->stamp == (iter)->stamp) - -static void gtk_tree_store_tree_model_init (GtkTreeModelIface *iface); -static void gtk_tree_store_drag_source_init(GtkTreeDragSourceIface *iface); -static void gtk_tree_store_drag_dest_init (GtkTreeDragDestIface *iface); -static void gtk_tree_store_sortable_init (GtkTreeSortableIface *iface); -static void gtk_tree_store_buildable_init (GtkBuildableIface *iface); -static void gtk_tree_store_finalize (GObject *object); -static GtkTreeModelFlags gtk_tree_store_get_flags (GtkTreeModel *tree_model); -static int gtk_tree_store_get_n_columns (GtkTreeModel *tree_model); -static GType gtk_tree_store_get_column_type (GtkTreeModel *tree_model, - int index); -static gboolean gtk_tree_store_get_iter (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreePath *path); -static GtkTreePath *gtk_tree_store_get_path (GtkTreeModel *tree_model, - GtkTreeIter *iter); -static void gtk_tree_store_get_value (GtkTreeModel *tree_model, - GtkTreeIter *iter, - int column, - GValue *value); -static gboolean gtk_tree_store_iter_next (GtkTreeModel *tree_model, - GtkTreeIter *iter); -static gboolean gtk_tree_store_iter_previous (GtkTreeModel *tree_model, - GtkTreeIter *iter); -static gboolean gtk_tree_store_iter_children (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent); -static gboolean gtk_tree_store_iter_has_child (GtkTreeModel *tree_model, - GtkTreeIter *iter); -static int gtk_tree_store_iter_n_children (GtkTreeModel *tree_model, - GtkTreeIter *iter); -static gboolean gtk_tree_store_iter_nth_child (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent, - int n); -static gboolean gtk_tree_store_iter_parent (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *child); - - -static void gtk_tree_store_set_n_columns (GtkTreeStore *tree_store, - int n_columns); -static void gtk_tree_store_set_column_type (GtkTreeStore *tree_store, - int column, - GType type); - -static void gtk_tree_store_increment_stamp (GtkTreeStore *tree_store); - - -/* DND interfaces */ -static gboolean real_gtk_tree_store_row_draggable (GtkTreeDragSource *drag_source, - GtkTreePath *path); -static gboolean gtk_tree_store_drag_data_delete (GtkTreeDragSource *drag_source, - GtkTreePath *path); -static GdkContentProvider * - gtk_tree_store_drag_data_get (GtkTreeDragSource *drag_source, - GtkTreePath *path); -static gboolean gtk_tree_store_drag_data_received (GtkTreeDragDest *drag_dest, - GtkTreePath *dest, - const GValue *value); -static gboolean gtk_tree_store_row_drop_possible (GtkTreeDragDest *drag_dest, - GtkTreePath *dest_path, - const GValue *value); - -/* Sortable Interfaces */ - -static void gtk_tree_store_sort (GtkTreeStore *tree_store); -static void gtk_tree_store_sort_iter_changed (GtkTreeStore *tree_store, - GtkTreeIter *iter, - int column, - gboolean emit_signal); -static gboolean gtk_tree_store_get_sort_column_id (GtkTreeSortable *sortable, - int *sort_column_id, - GtkSortType *order); -static void gtk_tree_store_set_sort_column_id (GtkTreeSortable *sortable, - int sort_column_id, - GtkSortType order); -static void gtk_tree_store_set_sort_func (GtkTreeSortable *sortable, - int sort_column_id, - GtkTreeIterCompareFunc func, - gpointer data, - GDestroyNotify destroy); -static void gtk_tree_store_set_default_sort_func (GtkTreeSortable *sortable, - GtkTreeIterCompareFunc func, - gpointer data, - GDestroyNotify destroy); -static gboolean gtk_tree_store_has_default_sort_func (GtkTreeSortable *sortable); - - -/* buildable */ - -static gboolean gtk_tree_store_buildable_custom_tag_start (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const char *tagname, - GtkBuildableParser *parser, - gpointer *data); -static void gtk_tree_store_buildable_custom_tag_end (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const char *tagname, - gpointer parser_data); - -static void gtk_tree_store_move (GtkTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *position, - gboolean before); - - -#ifdef G_ENABLE_DEBUG -static void validate_gnode (GNode *node); - -static inline void -validate_tree (GtkTreeStore *tree_store) -{ - if (GTK_DEBUG_CHECK (TREE)) - { - g_assert (G_NODE (tree_store->priv->root)->parent == NULL); - validate_gnode (G_NODE (tree_store->priv->root)); - } -} -#else -#define validate_tree(store) -#endif - -G_DEFINE_TYPE_WITH_CODE (GtkTreeStore, gtk_tree_store, G_TYPE_OBJECT, - G_ADD_PRIVATE (GtkTreeStore) - G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, - gtk_tree_store_tree_model_init) - G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE, - gtk_tree_store_drag_source_init) - G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_DEST, - gtk_tree_store_drag_dest_init) - G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_SORTABLE, - gtk_tree_store_sortable_init) - G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, - gtk_tree_store_buildable_init)) - -static void -gtk_tree_store_class_init (GtkTreeStoreClass *class) -{ - GObjectClass *object_class; - - object_class = (GObjectClass *) class; - - object_class->finalize = gtk_tree_store_finalize; -} - -static void -gtk_tree_store_tree_model_init (GtkTreeModelIface *iface) -{ - iface->get_flags = gtk_tree_store_get_flags; - iface->get_n_columns = gtk_tree_store_get_n_columns; - iface->get_column_type = gtk_tree_store_get_column_type; - iface->get_iter = gtk_tree_store_get_iter; - iface->get_path = gtk_tree_store_get_path; - iface->get_value = gtk_tree_store_get_value; - iface->iter_next = gtk_tree_store_iter_next; - iface->iter_previous = gtk_tree_store_iter_previous; - iface->iter_children = gtk_tree_store_iter_children; - iface->iter_has_child = gtk_tree_store_iter_has_child; - iface->iter_n_children = gtk_tree_store_iter_n_children; - iface->iter_nth_child = gtk_tree_store_iter_nth_child; - iface->iter_parent = gtk_tree_store_iter_parent; -} - -static void -gtk_tree_store_drag_source_init (GtkTreeDragSourceIface *iface) -{ - iface->row_draggable = real_gtk_tree_store_row_draggable; - iface->drag_data_delete = gtk_tree_store_drag_data_delete; - iface->drag_data_get = gtk_tree_store_drag_data_get; -} - -static void -gtk_tree_store_drag_dest_init (GtkTreeDragDestIface *iface) -{ - iface->drag_data_received = gtk_tree_store_drag_data_received; - iface->row_drop_possible = gtk_tree_store_row_drop_possible; -} - -static void -gtk_tree_store_sortable_init (GtkTreeSortableIface *iface) -{ - iface->get_sort_column_id = gtk_tree_store_get_sort_column_id; - iface->set_sort_column_id = gtk_tree_store_set_sort_column_id; - iface->set_sort_func = gtk_tree_store_set_sort_func; - iface->set_default_sort_func = gtk_tree_store_set_default_sort_func; - iface->has_default_sort_func = gtk_tree_store_has_default_sort_func; -} - -void -gtk_tree_store_buildable_init (GtkBuildableIface *iface) -{ - iface->custom_tag_start = gtk_tree_store_buildable_custom_tag_start; - iface->custom_tag_end = gtk_tree_store_buildable_custom_tag_end; -} - -static void -gtk_tree_store_init (GtkTreeStore *tree_store) -{ - GtkTreeStorePrivate *priv; - - priv = gtk_tree_store_get_instance_private (tree_store); - tree_store->priv = priv; - priv->root = g_node_new (NULL); - /* While the odds are against us getting 0... */ - do - { - priv->stamp = g_random_int (); - } - while (priv->stamp == 0); - - priv->sort_list = NULL; - priv->sort_column_id = GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID; - priv->columns_dirty = FALSE; -} - -/** - * gtk_tree_store_new: - * @n_columns: number of columns in the tree store - * @...: all `GType` types for the columns, from first to last - * - * Creates a new tree store as with @n_columns columns each of the types passed - * in. Note that only types derived from standard GObject fundamental types - * are supported. - * - * As an example, - * - * ``` - * gtk_tree_store_new (3, G_TYPE_INT, G_TYPE_STRING, GDK_TYPE_TEXTURE); - * ``` - * - * will create a new `GtkTreeStore` with three columns, of type - * `int`, `gchararray`, and `GdkTexture` respectively. - * - * Returns: a new `GtkTreeStore` - **/ -GtkTreeStore * -gtk_tree_store_new (int n_columns, - ...) -{ - GtkTreeStore *retval; - va_list args; - int i; - - g_return_val_if_fail (n_columns > 0, NULL); - - retval = g_object_new (GTK_TYPE_TREE_STORE, NULL); - gtk_tree_store_set_n_columns (retval, n_columns); - - va_start (args, n_columns); - - for (i = 0; i < n_columns; i++) - { - GType type = va_arg (args, GType); - if (! _gtk_tree_data_list_check_type (type)) - { - g_warning ("%s: Invalid type %s", G_STRLOC, g_type_name (type)); - g_object_unref (retval); - va_end (args); - return NULL; - } - gtk_tree_store_set_column_type (retval, i, type); - } - va_end (args); - - return retval; -} -/** - * gtk_tree_store_newv: (rename-to gtk_tree_store_new) - * @n_columns: number of columns in the tree store - * @types: (array length=n_columns): an array of `GType` types for the columns, from first to last - * - * Non vararg creation function. Used primarily by language bindings. - * - * Returns: (transfer full): a new `GtkTreeStore` - **/ -GtkTreeStore * -gtk_tree_store_newv (int n_columns, - GType *types) -{ - GtkTreeStore *retval; - int i; - - g_return_val_if_fail (n_columns > 0, NULL); - - retval = g_object_new (GTK_TYPE_TREE_STORE, NULL); - gtk_tree_store_set_n_columns (retval, n_columns); - - for (i = 0; i < n_columns; i++) - { - if (! _gtk_tree_data_list_check_type (types[i])) - { - g_warning ("%s: Invalid type %s", G_STRLOC, g_type_name (types[i])); - g_object_unref (retval); - return NULL; - } - gtk_tree_store_set_column_type (retval, i, types[i]); - } - - return retval; -} - - -/** - * gtk_tree_store_set_column_types: - * @tree_store: A `GtkTreeStore` - * @n_columns: Number of columns for the tree store - * @types: (array length=n_columns): An array of `GType` types, one for each column - * - * This function is meant primarily for `GObjects` that inherit from - * `GtkTreeStore`, and should only be used when constructing a new - * `GtkTreeStore`. It will not function after a row has been added, - * or a method on the `GtkTreeModel` interface is called. - **/ -void -gtk_tree_store_set_column_types (GtkTreeStore *tree_store, - int n_columns, - GType *types) -{ - int i; - - g_return_if_fail (GTK_IS_TREE_STORE (tree_store)); - g_return_if_fail (tree_store->priv->columns_dirty == 0); - - gtk_tree_store_set_n_columns (tree_store, n_columns); - for (i = 0; i < n_columns; i++) - { - if (! _gtk_tree_data_list_check_type (types[i])) - { - g_warning ("%s: Invalid type %s", G_STRLOC, g_type_name (types[i])); - continue; - } - gtk_tree_store_set_column_type (tree_store, i, types[i]); - } -} - -static void -gtk_tree_store_set_n_columns (GtkTreeStore *tree_store, - int n_columns) -{ - GtkTreeStorePrivate *priv = tree_store->priv; - int i; - - if (priv->n_columns == n_columns) - return; - - priv->column_headers = g_renew (GType, priv->column_headers, n_columns); - for (i = priv->n_columns; i < n_columns; i++) - priv->column_headers[i] = G_TYPE_INVALID; - priv->n_columns = n_columns; - - if (priv->sort_list) - _gtk_tree_data_list_header_free (priv->sort_list); - - priv->sort_list = _gtk_tree_data_list_header_new (n_columns, priv->column_headers); -} - -/** - * gtk_tree_store_set_column_type: - * @tree_store: a `GtkTreeStore` - * @column: column number - * @type: type of the data to be stored in @column - * - * Supported types include: %G_TYPE_UINT, %G_TYPE_INT, %G_TYPE_UCHAR, - * %G_TYPE_CHAR, %G_TYPE_BOOLEAN, %G_TYPE_POINTER, %G_TYPE_FLOAT, - * %G_TYPE_DOUBLE, %G_TYPE_STRING, %G_TYPE_OBJECT, and %G_TYPE_BOXED, along with - * subclasses of those types such as %GDK_TYPE_PIXBUF. - * - **/ -static void -gtk_tree_store_set_column_type (GtkTreeStore *tree_store, - int column, - GType type) -{ - GtkTreeStorePrivate *priv = tree_store->priv; - - if (!_gtk_tree_data_list_check_type (type)) - { - g_warning ("%s: Invalid type %s", G_STRLOC, g_type_name (type)); - return; - } - priv->column_headers[column] = type; -} - -static gboolean -node_free (GNode *node, gpointer data) -{ - if (node->data) - _gtk_tree_data_list_free (node->data, (GType*)data); - node->data = NULL; - - return FALSE; -} - -static void -gtk_tree_store_finalize (GObject *object) -{ - GtkTreeStore *tree_store = GTK_TREE_STORE (object); - GtkTreeStorePrivate *priv = tree_store->priv; - - g_node_traverse (priv->root, G_POST_ORDER, G_TRAVERSE_ALL, -1, - node_free, priv->column_headers); - g_node_destroy (priv->root); - _gtk_tree_data_list_header_free (priv->sort_list); - g_free (priv->column_headers); - - if (priv->default_sort_destroy) - { - GDestroyNotify d = priv->default_sort_destroy; - - priv->default_sort_destroy = NULL; - d (priv->default_sort_data); - priv->default_sort_data = NULL; - } - - /* must chain up */ - G_OBJECT_CLASS (gtk_tree_store_parent_class)->finalize (object); -} - -/* fulfill the GtkTreeModel requirements */ -/* NOTE: GtkTreeStore::root is a GNode, that acts as the parent node. However, - * it is not visible to the tree or to the user., and the path “0” refers to the - * first child of GtkTreeStore::root. - */ - - -static GtkTreeModelFlags -gtk_tree_store_get_flags (GtkTreeModel *tree_model) -{ - return GTK_TREE_MODEL_ITERS_PERSIST; -} - -static int -gtk_tree_store_get_n_columns (GtkTreeModel *tree_model) -{ - GtkTreeStore *tree_store = (GtkTreeStore *) tree_model; - GtkTreeStorePrivate *priv = tree_store->priv; - - priv->columns_dirty = TRUE; - - return priv->n_columns; -} - -static GType -gtk_tree_store_get_column_type (GtkTreeModel *tree_model, - int index) -{ - GtkTreeStore *tree_store = (GtkTreeStore *) tree_model; - GtkTreeStorePrivate *priv = tree_store->priv; - - g_return_val_if_fail (index < priv->n_columns, G_TYPE_INVALID); - - priv->columns_dirty = TRUE; - - return priv->column_headers[index]; -} - -static gboolean -gtk_tree_store_get_iter (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreePath *path) -{ - GtkTreeStore *tree_store = (GtkTreeStore *) tree_model; - GtkTreeStorePrivate *priv = tree_store->priv; - GtkTreeIter parent; - int *indices; - int depth, i; - - priv->columns_dirty = TRUE; - - indices = gtk_tree_path_get_indices (path); - depth = gtk_tree_path_get_depth (path); - - g_return_val_if_fail (depth > 0, FALSE); - - parent.stamp = priv->stamp; - parent.user_data = priv->root; - - if (!gtk_tree_store_iter_nth_child (tree_model, iter, &parent, indices[0])) - { - iter->stamp = 0; - return FALSE; - } - - for (i = 1; i < depth; i++) - { - parent = *iter; - if (!gtk_tree_store_iter_nth_child (tree_model, iter, &parent, indices[i])) - { - iter->stamp = 0; - return FALSE; - } - } - - return TRUE; -} - -static GtkTreePath * -gtk_tree_store_get_path (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - GtkTreeStore *tree_store = (GtkTreeStore *) tree_model; - GtkTreeStorePrivate *priv = tree_store->priv; - GtkTreePath *retval; - GNode *tmp_node; - int i = 0; - - g_return_val_if_fail (iter->user_data != NULL, NULL); - g_return_val_if_fail (iter->stamp == priv->stamp, NULL); - - validate_tree (tree_store); - - if (G_NODE (iter->user_data)->parent == NULL && - G_NODE (iter->user_data) == priv->root) - return gtk_tree_path_new (); - g_assert (G_NODE (iter->user_data)->parent != NULL); - - if (G_NODE (iter->user_data)->parent == G_NODE (priv->root)) - { - retval = gtk_tree_path_new (); - tmp_node = G_NODE (priv->root)->children; - } - else - { - GtkTreeIter tmp_iter = *iter; - - tmp_iter.user_data = G_NODE (iter->user_data)->parent; - - retval = gtk_tree_store_get_path (tree_model, &tmp_iter); - tmp_node = G_NODE (iter->user_data)->parent->children; - } - - if (retval == NULL) - return NULL; - - if (tmp_node == NULL) - { - gtk_tree_path_free (retval); - return NULL; - } - - for (; tmp_node; tmp_node = tmp_node->next) - { - if (tmp_node == G_NODE (iter->user_data)) - break; - i++; - } - - if (tmp_node == NULL) - { - /* We couldn't find node, meaning it's prolly not ours */ - /* Perhaps I should do a g_return_if_fail here. */ - gtk_tree_path_free (retval); - return NULL; - } - - gtk_tree_path_append_index (retval, i); - - return retval; -} - - -static void -gtk_tree_store_get_value (GtkTreeModel *tree_model, - GtkTreeIter *iter, - int column, - GValue *value) -{ - GtkTreeStore *tree_store = (GtkTreeStore *) tree_model; - GtkTreeStorePrivate *priv = tree_store->priv; - GtkTreeDataList *list; - int tmp_column = column; - - g_return_if_fail (column < priv->n_columns); - g_return_if_fail (VALID_ITER (iter, tree_store)); - - list = G_NODE (iter->user_data)->data; - - while (tmp_column-- > 0 && list) - list = list->next; - - if (list) - { - _gtk_tree_data_list_node_to_value (list, - priv->column_headers[column], - value); - } - else - { - /* We want to return an initialized but empty (default) value */ - g_value_init (value, priv->column_headers[column]); - } -} - -static gboolean -gtk_tree_store_iter_next (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - g_return_val_if_fail (iter->user_data != NULL, FALSE); - g_return_val_if_fail (iter->stamp == GTK_TREE_STORE (tree_model)->priv->stamp, FALSE); - - if (G_NODE (iter->user_data)->next == NULL) - { - iter->stamp = 0; - return FALSE; - } - - iter->user_data = G_NODE (iter->user_data)->next; - - return TRUE; -} - -static gboolean -gtk_tree_store_iter_previous (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - g_return_val_if_fail (iter->user_data != NULL, FALSE); - g_return_val_if_fail (iter->stamp == GTK_TREE_STORE (tree_model)->priv->stamp, FALSE); - - if (G_NODE (iter->user_data)->prev == NULL) - { - iter->stamp = 0; - return FALSE; - } - - iter->user_data = G_NODE (iter->user_data)->prev; - - return TRUE; -} - -static gboolean -gtk_tree_store_iter_children (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent) -{ - GtkTreeStore *tree_store = (GtkTreeStore *) tree_model; - GtkTreeStorePrivate *priv = tree_store->priv; - GNode *children; - - if (parent) - g_return_val_if_fail (VALID_ITER (parent, tree_store), FALSE); - - if (parent) - children = G_NODE (parent->user_data)->children; - else - children = G_NODE (priv->root)->children; - - if (children) - { - iter->stamp = priv->stamp; - iter->user_data = children; - return TRUE; - } - else - { - iter->stamp = 0; - return FALSE; - } -} - -static gboolean -gtk_tree_store_iter_has_child (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - g_return_val_if_fail (iter->user_data != NULL, FALSE); - g_return_val_if_fail (VALID_ITER (iter, tree_model), FALSE); - - return G_NODE (iter->user_data)->children != NULL; -} - -static int -gtk_tree_store_iter_n_children (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - GNode *node; - int i = 0; - - g_return_val_if_fail (iter == NULL || iter->user_data != NULL, 0); - - if (iter == NULL) - node = G_NODE (GTK_TREE_STORE (tree_model)->priv->root)->children; - else - node = G_NODE (iter->user_data)->children; - - while (node) - { - i++; - node = node->next; - } - - return i; -} - -static gboolean -gtk_tree_store_iter_nth_child (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent, - int n) -{ - GtkTreeStore *tree_store = (GtkTreeStore *) tree_model; - GtkTreeStorePrivate *priv = tree_store->priv; - GNode *parent_node; - GNode *child; - - g_return_val_if_fail (parent == NULL || parent->user_data != NULL, FALSE); - - if (parent == NULL) - parent_node = priv->root; - else - parent_node = parent->user_data; - - child = g_node_nth_child (parent_node, n); - - if (child) - { - iter->user_data = child; - iter->stamp = priv->stamp; - return TRUE; - } - else - { - iter->stamp = 0; - return FALSE; - } -} - -static gboolean -gtk_tree_store_iter_parent (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *child) -{ - GtkTreeStore *tree_store = (GtkTreeStore *) tree_model; - GtkTreeStorePrivate *priv = tree_store->priv; - GNode *parent; - - g_return_val_if_fail (iter != NULL, FALSE); - g_return_val_if_fail (VALID_ITER (child, tree_store), FALSE); - - parent = G_NODE (child->user_data)->parent; - - g_assert (parent != NULL); - - if (parent != priv->root) - { - iter->user_data = parent; - iter->stamp = priv->stamp; - return TRUE; - } - else - { - iter->stamp = 0; - return FALSE; - } -} - - -/* Does not emit a signal */ -static gboolean -gtk_tree_store_real_set_value (GtkTreeStore *tree_store, - GtkTreeIter *iter, - int column, - GValue *value, - gboolean sort) -{ - GtkTreeStorePrivate *priv = tree_store->priv; - GtkTreeDataList *list; - GtkTreeDataList *prev; - int old_column = column; - GValue real_value = G_VALUE_INIT; - gboolean converted = FALSE; - gboolean retval = FALSE; - - if (! g_type_is_a (G_VALUE_TYPE (value), priv->column_headers[column])) - { - if (! (g_value_type_transformable (G_VALUE_TYPE (value), priv->column_headers[column]))) - { - g_warning ("%s: Unable to convert from %s to %s", - G_STRLOC, - g_type_name (G_VALUE_TYPE (value)), - g_type_name (priv->column_headers[column])); - return retval; - } - - g_value_init (&real_value, priv->column_headers[column]); - if (!g_value_transform (value, &real_value)) - { - g_warning ("%s: Unable to make conversion from %s to %s", - G_STRLOC, - g_type_name (G_VALUE_TYPE (value)), - g_type_name (priv->column_headers[column])); - g_value_unset (&real_value); - return retval; - } - converted = TRUE; - } - - prev = list = G_NODE (iter->user_data)->data; - - while (list != NULL) - { - if (column == 0) - { - if (converted) - _gtk_tree_data_list_value_to_node (list, &real_value); - else - _gtk_tree_data_list_value_to_node (list, value); - retval = TRUE; - if (converted) - g_value_unset (&real_value); - if (sort && GTK_TREE_STORE_IS_SORTED (tree_store)) - gtk_tree_store_sort_iter_changed (tree_store, iter, old_column, TRUE); - return retval; - } - - column--; - prev = list; - list = list->next; - } - - if (G_NODE (iter->user_data)->data == NULL) - { - G_NODE (iter->user_data)->data = list = _gtk_tree_data_list_alloc (); - list->next = NULL; - } - else - { - list = prev->next = _gtk_tree_data_list_alloc (); - list->next = NULL; - } - - while (column != 0) - { - list->next = _gtk_tree_data_list_alloc (); - list = list->next; - list->next = NULL; - column --; - } - - if (converted) - _gtk_tree_data_list_value_to_node (list, &real_value); - else - _gtk_tree_data_list_value_to_node (list, value); - - retval = TRUE; - if (converted) - g_value_unset (&real_value); - - if (sort && GTK_TREE_STORE_IS_SORTED (tree_store)) - gtk_tree_store_sort_iter_changed (tree_store, iter, old_column, TRUE); - - return retval; -} - -/** - * gtk_tree_store_set_value: - * @tree_store: a `GtkTreeStore` - * @iter: A valid `GtkTreeIter` for the row being modified - * @column: column number to modify - * @value: new value for the cell - * - * Sets the data in the cell specified by @iter and @column. - * The type of @value must be convertible to the type of the - * column. - * - **/ -void -gtk_tree_store_set_value (GtkTreeStore *tree_store, - GtkTreeIter *iter, - int column, - GValue *value) -{ - g_return_if_fail (GTK_IS_TREE_STORE (tree_store)); - g_return_if_fail (VALID_ITER (iter, tree_store)); - g_return_if_fail (column >= 0 && column < tree_store->priv->n_columns); - g_return_if_fail (G_IS_VALUE (value)); - - if (gtk_tree_store_real_set_value (tree_store, iter, column, value, TRUE)) - { - GtkTreePath *path; - - path = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter); - gtk_tree_model_row_changed (GTK_TREE_MODEL (tree_store), path, iter); - gtk_tree_path_free (path); - } -} - -static GtkTreeIterCompareFunc -gtk_tree_store_get_compare_func (GtkTreeStore *tree_store) -{ - GtkTreeStorePrivate *priv = tree_store->priv; - GtkTreeIterCompareFunc func = NULL; - - if (GTK_TREE_STORE_IS_SORTED (tree_store)) - { - if (priv->sort_column_id != -1) - { - GtkTreeDataSortHeader *header; - header = _gtk_tree_data_list_get_header (priv->sort_list, - priv->sort_column_id); - g_return_val_if_fail (header != NULL, NULL); - g_return_val_if_fail (header->func != NULL, NULL); - func = header->func; - } - else - { - func = priv->default_sort_func; - } - } - - return func; -} - -static void -gtk_tree_store_set_vector_internal (GtkTreeStore *tree_store, - GtkTreeIter *iter, - gboolean *emit_signal, - gboolean *maybe_need_sort, - int *columns, - GValue *values, - int n_values) -{ - GtkTreeStorePrivate *priv = tree_store->priv; - int i; - GtkTreeIterCompareFunc func = NULL; - - func = gtk_tree_store_get_compare_func (tree_store); - if (func != _gtk_tree_data_list_compare_func) - *maybe_need_sort = TRUE; - - for (i = 0; i < n_values; i++) - { - *emit_signal = gtk_tree_store_real_set_value (tree_store, iter, - columns[i], &values[i], - FALSE) || *emit_signal; - - if (func == _gtk_tree_data_list_compare_func && - columns[i] == priv->sort_column_id) - *maybe_need_sort = TRUE; - } -} - -static void -gtk_tree_store_set_valist_internal (GtkTreeStore *tree_store, - GtkTreeIter *iter, - gboolean *emit_signal, - gboolean *maybe_need_sort, - va_list var_args) -{ - GtkTreeStorePrivate *priv = tree_store->priv; - int column; - GtkTreeIterCompareFunc func = NULL; - - column = va_arg (var_args, int); - - func = gtk_tree_store_get_compare_func (tree_store); - if (func != _gtk_tree_data_list_compare_func) - *maybe_need_sort = TRUE; - - while (column != -1) - { - GValue value = G_VALUE_INIT; - char *error = NULL; - - if (column < 0 || column >= priv->n_columns) - { - g_warning ("%s: Invalid column number %d added to iter (remember to end your list of columns with a -1)", G_STRLOC, column); - break; - } - - G_VALUE_COLLECT_INIT (&value, priv->column_headers[column], - var_args, 0, &error); - if (error) - { - g_warning ("%s: %s", G_STRLOC, error); - g_free (error); - - /* we purposely leak the value here, it might not be - * in a sane state if an error condition occurred - */ - break; - } - - *emit_signal = gtk_tree_store_real_set_value (tree_store, - iter, - column, - &value, - FALSE) || *emit_signal; - - if (func == _gtk_tree_data_list_compare_func && - column == priv->sort_column_id) - *maybe_need_sort = TRUE; - - g_value_unset (&value); - - column = va_arg (var_args, int); - } -} - -/** - * gtk_tree_store_set_valuesv: (rename-to gtk_tree_store_set) - * @tree_store: A `GtkTreeStore` - * @iter: A valid `GtkTreeIter` for the row being modified - * @columns: (array length=n_values): an array of column numbers - * @values: (array length=n_values): an array of GValues - * @n_values: the length of the @columns and @values arrays - * - * A variant of gtk_tree_store_set_valist() which takes - * the columns and values as two arrays, instead of varargs. This - * function is mainly intended for language bindings or in case - * the number of columns to change is not known until run-time. - **/ -void -gtk_tree_store_set_valuesv (GtkTreeStore *tree_store, - GtkTreeIter *iter, - int *columns, - GValue *values, - int n_values) -{ - GtkTreeStorePrivate *priv = tree_store->priv; - gboolean emit_signal = FALSE; - gboolean maybe_need_sort = FALSE; - - g_return_if_fail (GTK_IS_TREE_STORE (tree_store)); - g_return_if_fail (VALID_ITER (iter, tree_store)); - - gtk_tree_store_set_vector_internal (tree_store, iter, - &emit_signal, - &maybe_need_sort, - columns, values, n_values); - - if (maybe_need_sort && GTK_TREE_STORE_IS_SORTED (tree_store)) - gtk_tree_store_sort_iter_changed (tree_store, iter, priv->sort_column_id, TRUE); - - if (emit_signal) - { - GtkTreePath *path; - - path = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter); - gtk_tree_model_row_changed (GTK_TREE_MODEL (tree_store), path, iter); - gtk_tree_path_free (path); - } -} - -/** - * gtk_tree_store_set_valist: - * @tree_store: A `GtkTreeStore` - * @iter: A valid `GtkTreeIter` for the row being modified - * @var_args: va_list of column/value pairs - * - * See gtk_tree_store_set(); this version takes a va_list for - * use by language bindings. - * - **/ -void -gtk_tree_store_set_valist (GtkTreeStore *tree_store, - GtkTreeIter *iter, - va_list var_args) -{ - GtkTreeStorePrivate *priv = tree_store->priv; - gboolean emit_signal = FALSE; - gboolean maybe_need_sort = FALSE; - - g_return_if_fail (GTK_IS_TREE_STORE (tree_store)); - g_return_if_fail (VALID_ITER (iter, tree_store)); - - gtk_tree_store_set_valist_internal (tree_store, iter, - &emit_signal, - &maybe_need_sort, - var_args); - - if (maybe_need_sort && GTK_TREE_STORE_IS_SORTED (tree_store)) - gtk_tree_store_sort_iter_changed (tree_store, iter, priv->sort_column_id, TRUE); - - if (emit_signal) - { - GtkTreePath *path; - - path = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter); - gtk_tree_model_row_changed (GTK_TREE_MODEL (tree_store), path, iter); - gtk_tree_path_free (path); - } -} - -/** - * gtk_tree_store_set: - * @tree_store: A `GtkTreeStore` - * @iter: A valid `GtkTreeIter` for the row being modified - * @...: pairs of column number and value, terminated with -1 - * - * Sets the value of one or more cells in the row referenced by @iter. - * The variable argument list should contain integer column numbers, - * each column number followed by the value to be set. - * The list is terminated by a -1. For example, to set column 0 with type - * %G_TYPE_STRING to “Foo”, you would write - * `gtk_tree_store_set (store, iter, 0, "Foo", -1)`. - * - * The value will be referenced by the store if it is a %G_TYPE_OBJECT, and it - * will be copied if it is a %G_TYPE_STRING or %G_TYPE_BOXED. - **/ -void -gtk_tree_store_set (GtkTreeStore *tree_store, - GtkTreeIter *iter, - ...) -{ - va_list var_args; - - va_start (var_args, iter); - gtk_tree_store_set_valist (tree_store, iter, var_args); - va_end (var_args); -} - -/** - * gtk_tree_store_remove: - * @tree_store: A `GtkTreeStore` - * @iter: A valid `GtkTreeIter` - * - * Removes @iter from @tree_store. After being removed, @iter is set to the - * next valid row at that level, or invalidated if it previously pointed to the - * last one. - * - * Returns: %TRUE if @iter is still valid, %FALSE if not. - **/ -gboolean -gtk_tree_store_remove (GtkTreeStore *tree_store, - GtkTreeIter *iter) -{ - GtkTreeStorePrivate *priv = tree_store->priv; - GtkTreePath *path; - GtkTreeIter new_iter = {0,}; - GNode *parent; - GNode *next_node; - - g_return_val_if_fail (GTK_IS_TREE_STORE (tree_store), FALSE); - g_return_val_if_fail (VALID_ITER (iter, tree_store), FALSE); - - parent = G_NODE (iter->user_data)->parent; - - g_assert (parent != NULL); - next_node = G_NODE (iter->user_data)->next; - - if (G_NODE (iter->user_data)->data) - g_node_traverse (G_NODE (iter->user_data), G_POST_ORDER, G_TRAVERSE_ALL, - -1, node_free, priv->column_headers); - - path = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter); - g_node_destroy (G_NODE (iter->user_data)); - - gtk_tree_model_row_deleted (GTK_TREE_MODEL (tree_store), path); - - if (parent != G_NODE (priv->root)) - { - /* child_toggled */ - if (parent->children == NULL) - { - gtk_tree_path_up (path); - - new_iter.stamp = priv->stamp; - new_iter.user_data = parent; - gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (tree_store), path, &new_iter); - } - } - gtk_tree_path_free (path); - - /* revalidate iter */ - if (next_node != NULL) - { - iter->stamp = priv->stamp; - iter->user_data = next_node; - return TRUE; - } - else - { - iter->stamp = 0; - iter->user_data = NULL; - } - - return FALSE; -} - -/** - * gtk_tree_store_insert: - * @tree_store: A `GtkTreeStore` - * @iter: (out): An unset `GtkTreeIter` to set to the new row - * @parent: (nullable): A valid `GtkTreeIter` - * @position: position to insert the new row, or -1 for last - * - * Creates a new row at @position. If parent is non-%NULL, then the row will be - * made a child of @parent. Otherwise, the row will be created at the toplevel. - * If @position is -1 or is larger than the number of rows at that level, then - * the new row will be inserted to the end of the list. @iter will be changed - * to point to this new row. The row will be empty after this function is - * called. To fill in values, you need to call gtk_tree_store_set() or - * gtk_tree_store_set_value(). - * - **/ -void -gtk_tree_store_insert (GtkTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *parent, - int position) -{ - GtkTreeStorePrivate *priv = tree_store->priv; - GtkTreePath *path; - GNode *parent_node; - GNode *new_node; - - g_return_if_fail (GTK_IS_TREE_STORE (tree_store)); - g_return_if_fail (iter != NULL); - if (parent) - g_return_if_fail (VALID_ITER (parent, tree_store)); - - if (parent) - parent_node = parent->user_data; - else - parent_node = priv->root; - - priv->columns_dirty = TRUE; - - new_node = g_node_new (NULL); - - iter->stamp = priv->stamp; - iter->user_data = new_node; - g_node_insert (parent_node, position, new_node); - - path = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter); - gtk_tree_model_row_inserted (GTK_TREE_MODEL (tree_store), path, iter); - - if (parent_node != priv->root) - { - if (new_node->prev == NULL && new_node->next == NULL) - { - gtk_tree_path_up (path); - gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (tree_store), path, parent); - } - } - - gtk_tree_path_free (path); - - validate_tree ((GtkTreeStore*)tree_store); -} - -/** - * gtk_tree_store_insert_before: - * @tree_store: A `GtkTreeStore` - * @iter: (out): An unset `GtkTreeIter` to set to the new row - * @parent: (nullable): A valid `GtkTreeIter` - * @sibling: (nullable): A valid `GtkTreeIter` - * - * Inserts a new row before @sibling. If @sibling is %NULL, then the row will - * be appended to @parent ’s children. If @parent and @sibling are %NULL, then - * the row will be appended to the toplevel. If both @sibling and @parent are - * set, then @parent must be the parent of @sibling. When @sibling is set, - * @parent is optional. - * - * @iter will be changed to point to this new row. The row will be empty after - * this function is called. To fill in values, you need to call - * gtk_tree_store_set() or gtk_tree_store_set_value(). - * - **/ -void -gtk_tree_store_insert_before (GtkTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *parent, - GtkTreeIter *sibling) -{ - GtkTreeStorePrivate *priv = tree_store->priv; - GtkTreePath *path; - GNode *parent_node = NULL; - GNode *new_node; - - g_return_if_fail (GTK_IS_TREE_STORE (tree_store)); - g_return_if_fail (iter != NULL); - if (parent != NULL) - g_return_if_fail (VALID_ITER (parent, tree_store)); - if (sibling != NULL) - g_return_if_fail (VALID_ITER (sibling, tree_store)); - - if (parent == NULL && sibling == NULL) - parent_node = priv->root; - else if (parent == NULL) - parent_node = G_NODE (sibling->user_data)->parent; - else if (sibling == NULL) - parent_node = G_NODE (parent->user_data); - else - { - g_return_if_fail (G_NODE (sibling->user_data)->parent == G_NODE (parent->user_data)); - parent_node = G_NODE (parent->user_data); - } - - priv->columns_dirty = TRUE; - - new_node = g_node_new (NULL); - - g_node_insert_before (parent_node, - sibling ? G_NODE (sibling->user_data) : NULL, - new_node); - - iter->stamp = priv->stamp; - iter->user_data = new_node; - - path = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter); - gtk_tree_model_row_inserted (GTK_TREE_MODEL (tree_store), path, iter); - - if (parent_node != priv->root) - { - if (new_node->prev == NULL && new_node->next == NULL) - { - GtkTreeIter parent_iter; - - parent_iter.stamp = priv->stamp; - parent_iter.user_data = parent_node; - - gtk_tree_path_up (path); - gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (tree_store), path, &parent_iter); - } - } - - gtk_tree_path_free (path); - - validate_tree (tree_store); -} - -/** - * gtk_tree_store_insert_after: - * @tree_store: A `GtkTreeStore` - * @iter: (out): An unset `GtkTreeIter` to set to the new row - * @parent: (nullable): A valid `GtkTreeIter` - * @sibling: (nullable): A valid `GtkTreeIter` - * - * Inserts a new row after @sibling. If @sibling is %NULL, then the row will be - * prepended to @parent ’s children. If @parent and @sibling are %NULL, then - * the row will be prepended to the toplevel. If both @sibling and @parent are - * set, then @parent must be the parent of @sibling. When @sibling is set, - * @parent is optional. - * - * @iter will be changed to point to this new row. The row will be empty after - * this function is called. To fill in values, you need to call - * gtk_tree_store_set() or gtk_tree_store_set_value(). - * - **/ -void -gtk_tree_store_insert_after (GtkTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *parent, - GtkTreeIter *sibling) -{ - GtkTreeStorePrivate *priv = tree_store->priv; - GtkTreePath *path; - GNode *parent_node; - GNode *new_node; - - g_return_if_fail (GTK_IS_TREE_STORE (tree_store)); - g_return_if_fail (iter != NULL); - if (parent != NULL) - g_return_if_fail (VALID_ITER (parent, tree_store)); - if (sibling != NULL) - g_return_if_fail (VALID_ITER (sibling, tree_store)); - - if (parent == NULL && sibling == NULL) - parent_node = priv->root; - else if (parent == NULL) - parent_node = G_NODE (sibling->user_data)->parent; - else if (sibling == NULL) - parent_node = G_NODE (parent->user_data); - else - { - g_return_if_fail (G_NODE (sibling->user_data)->parent == - G_NODE (parent->user_data)); - parent_node = G_NODE (parent->user_data); - } - - priv->columns_dirty = TRUE; - - new_node = g_node_new (NULL); - - g_node_insert_after (parent_node, - sibling ? G_NODE (sibling->user_data) : NULL, - new_node); - - iter->stamp = priv->stamp; - iter->user_data = new_node; - - path = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter); - gtk_tree_model_row_inserted (GTK_TREE_MODEL (tree_store), path, iter); - - if (parent_node != priv->root) - { - if (new_node->prev == NULL && new_node->next == NULL) - { - GtkTreeIter parent_iter; - - parent_iter.stamp = priv->stamp; - parent_iter.user_data = parent_node; - - gtk_tree_path_up (path); - gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (tree_store), path, &parent_iter); - } - } - - gtk_tree_path_free (path); - - validate_tree (tree_store); -} - -/** - * gtk_tree_store_insert_with_values: - * @tree_store: A `GtkTreeStore` - * @iter: (out) (optional): An unset `GtkTreeIter` to set the new row - * @parent: (nullable): A valid `GtkTreeIter` - * @position: position to insert the new row, or -1 to append after existing rows - * @...: pairs of column number and value, terminated with -1 - * - * Creates a new row at @position. @iter will be changed to point to this - * new row. If @position is -1, or larger than the number of rows on the list, then - * the new row will be appended to the list. The row will be filled with - * the values given to this function. - * - * Calling - * `gtk_tree_store_insert_with_values (tree_store, iter, position, ...)` - * has the same effect as calling - * |[ - * gtk_tree_store_insert (tree_store, iter, position); - * gtk_tree_store_set (tree_store, iter, ...); - * ]| - * with the different that the former will only emit a row_inserted signal, - * while the latter will emit row_inserted, row_changed and if the tree store - * is sorted, rows_reordered. Since emitting the rows_reordered signal - * repeatedly can affect the performance of the program, - * gtk_tree_store_insert_with_values() should generally be preferred when - * inserting rows in a sorted tree store. - */ -void -gtk_tree_store_insert_with_values (GtkTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *parent, - int position, - ...) -{ - GtkTreeStorePrivate *priv = tree_store->priv; - GtkTreePath *path; - GNode *parent_node; - GNode *new_node; - GtkTreeIter tmp_iter; - va_list var_args; - gboolean changed = FALSE; - gboolean maybe_need_sort = FALSE; - - g_return_if_fail (GTK_IS_TREE_STORE (tree_store)); - - if (!iter) - iter = &tmp_iter; - - if (parent) - g_return_if_fail (VALID_ITER (parent, tree_store)); - - if (parent) - parent_node = parent->user_data; - else - parent_node = priv->root; - - priv->columns_dirty = TRUE; - - new_node = g_node_new (NULL); - - iter->stamp = priv->stamp; - iter->user_data = new_node; - g_node_insert (parent_node, position, new_node); - - va_start (var_args, position); - gtk_tree_store_set_valist_internal (tree_store, iter, - &changed, &maybe_need_sort, - var_args); - va_end (var_args); - - if (maybe_need_sort && GTK_TREE_STORE_IS_SORTED (tree_store)) - gtk_tree_store_sort_iter_changed (tree_store, iter, priv->sort_column_id, FALSE); - - path = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter); - gtk_tree_model_row_inserted (GTK_TREE_MODEL (tree_store), path, iter); - - if (parent_node != priv->root) - { - if (new_node->prev == NULL && new_node->next == NULL) - { - gtk_tree_path_up (path); - gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (tree_store), path, parent); - } - } - - gtk_tree_path_free (path); - - validate_tree ((GtkTreeStore *)tree_store); -} - -/** - * gtk_tree_store_insert_with_valuesv: (rename-to gtk_tree_store_insert_with_values) - * @tree_store: A `GtkTreeStore` - * @iter: (out) (optional): An unset `GtkTreeIter` to set the new row - * @parent: (nullable): A valid `GtkTreeIter` - * @position: position to insert the new row, or -1 for last - * @columns: (array length=n_values): an array of column numbers - * @values: (array length=n_values): an array of GValues - * @n_values: the length of the @columns and @values arrays - * - * A variant of gtk_tree_store_insert_with_values() which takes - * the columns and values as two arrays, instead of varargs. This - * function is mainly intended for language bindings. - */ -void -gtk_tree_store_insert_with_valuesv (GtkTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *parent, - int position, - int *columns, - GValue *values, - int n_values) -{ - GtkTreeStorePrivate *priv = tree_store->priv; - GtkTreePath *path; - GNode *parent_node; - GNode *new_node; - GtkTreeIter tmp_iter; - gboolean changed = FALSE; - gboolean maybe_need_sort = FALSE; - - g_return_if_fail (GTK_IS_TREE_STORE (tree_store)); - - if (!iter) - iter = &tmp_iter; - - if (parent) - g_return_if_fail (VALID_ITER (parent, tree_store)); - - if (parent) - parent_node = parent->user_data; - else - parent_node = priv->root; - - priv->columns_dirty = TRUE; - - new_node = g_node_new (NULL); - - iter->stamp = priv->stamp; - iter->user_data = new_node; - g_node_insert (parent_node, position, new_node); - - gtk_tree_store_set_vector_internal (tree_store, iter, - &changed, &maybe_need_sort, - columns, values, n_values); - - if (maybe_need_sort && GTK_TREE_STORE_IS_SORTED (tree_store)) - gtk_tree_store_sort_iter_changed (tree_store, iter, priv->sort_column_id, FALSE); - - path = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter); - gtk_tree_model_row_inserted (GTK_TREE_MODEL (tree_store), path, iter); - - if (parent_node != priv->root) - { - if (new_node->prev == NULL && new_node->next == NULL) - { - gtk_tree_path_up (path); - gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (tree_store), path, parent); - } - } - - gtk_tree_path_free (path); - - validate_tree ((GtkTreeStore *)tree_store); -} - -/** - * gtk_tree_store_prepend: - * @tree_store: A `GtkTreeStore` - * @iter: (out): An unset `GtkTreeIter` to set to the prepended row - * @parent: (nullable): A valid `GtkTreeIter` - * - * Prepends a new row to @tree_store. If @parent is non-%NULL, then it will prepend - * the new row before the first child of @parent, otherwise it will prepend a row - * to the top level. @iter will be changed to point to this new row. The row - * will be empty after this function is called. To fill in values, you need to - * call gtk_tree_store_set() or gtk_tree_store_set_value(). - **/ -void -gtk_tree_store_prepend (GtkTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *parent) -{ - GtkTreeStorePrivate *priv = tree_store->priv; - GNode *parent_node; - - g_return_if_fail (GTK_IS_TREE_STORE (tree_store)); - g_return_if_fail (iter != NULL); - if (parent != NULL) - g_return_if_fail (VALID_ITER (parent, tree_store)); - - priv->columns_dirty = TRUE; - - if (parent == NULL) - parent_node = priv->root; - else - parent_node = parent->user_data; - - if (parent_node->children == NULL) - { - GtkTreePath *path; - - iter->stamp = priv->stamp; - iter->user_data = g_node_new (NULL); - - g_node_prepend (parent_node, G_NODE (iter->user_data)); - - path = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter); - gtk_tree_model_row_inserted (GTK_TREE_MODEL (tree_store), path, iter); - - if (parent_node != priv->root) - { - gtk_tree_path_up (path); - gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (tree_store), path, parent); - } - gtk_tree_path_free (path); - } - else - { - gtk_tree_store_insert_after (tree_store, iter, parent, NULL); - } - - validate_tree (tree_store); -} - -/** - * gtk_tree_store_append: - * @tree_store: A `GtkTreeStore` - * @iter: (out): An unset `GtkTreeIter` to set to the appended row - * @parent: (nullable): A valid `GtkTreeIter` - * - * Appends a new row to @tree_store. If @parent is non-%NULL, then it will append the - * new row after the last child of @parent, otherwise it will append a row to - * the top level. @iter will be changed to point to this new row. The row will - * be empty after this function is called. To fill in values, you need to call - * gtk_tree_store_set() or gtk_tree_store_set_value(). - **/ -void -gtk_tree_store_append (GtkTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *parent) -{ - GtkTreeStorePrivate *priv = tree_store->priv; - GNode *parent_node; - - g_return_if_fail (GTK_IS_TREE_STORE (tree_store)); - g_return_if_fail (iter != NULL); - if (parent != NULL) - g_return_if_fail (VALID_ITER (parent, tree_store)); - - if (parent == NULL) - parent_node = priv->root; - else - parent_node = parent->user_data; - - priv->columns_dirty = TRUE; - - if (parent_node->children == NULL) - { - GtkTreePath *path; - - iter->stamp = priv->stamp; - iter->user_data = g_node_new (NULL); - - g_node_append (parent_node, G_NODE (iter->user_data)); - - path = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter); - gtk_tree_model_row_inserted (GTK_TREE_MODEL (tree_store), path, iter); - - if (parent_node != priv->root) - { - gtk_tree_path_up (path); - gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (tree_store), path, parent); - } - gtk_tree_path_free (path); - } - else - { - gtk_tree_store_insert_before (tree_store, iter, parent, NULL); - } - - validate_tree (tree_store); -} - -/** - * gtk_tree_store_is_ancestor: - * @tree_store: A `GtkTreeStore` - * @iter: A valid `GtkTreeIter` - * @descendant: A valid `GtkTreeIter` - * - * Returns %TRUE if @iter is an ancestor of @descendant. That is, @iter is the - * parent (or grandparent or great-grandparent) of @descendant. - * - * Returns: %TRUE, if @iter is an ancestor of @descendant - **/ -gboolean -gtk_tree_store_is_ancestor (GtkTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *descendant) -{ - g_return_val_if_fail (GTK_IS_TREE_STORE (tree_store), FALSE); - g_return_val_if_fail (VALID_ITER (iter, tree_store), FALSE); - g_return_val_if_fail (VALID_ITER (descendant, tree_store), FALSE); - - return g_node_is_ancestor (G_NODE (iter->user_data), - G_NODE (descendant->user_data)); -} - - -/** - * gtk_tree_store_iter_depth: - * @tree_store: A `GtkTreeStore` - * @iter: A valid `GtkTreeIter` - * - * Returns the depth of @iter. This will be 0 for anything on the root level, 1 - * for anything down a level, etc. - * - * Returns: The depth of @iter - **/ -int -gtk_tree_store_iter_depth (GtkTreeStore *tree_store, - GtkTreeIter *iter) -{ - g_return_val_if_fail (GTK_IS_TREE_STORE (tree_store), 0); - g_return_val_if_fail (VALID_ITER (iter, tree_store), 0); - - return g_node_depth (G_NODE (iter->user_data)) - 2; -} - -/* simple ripoff from g_node_traverse_post_order */ -static gboolean -gtk_tree_store_clear_traverse (GNode *node, - GtkTreeStore *store) -{ - GtkTreeIter iter; - - if (node->children) - { - GNode *child; - - child = node->children; - while (child) - { - register GNode *current; - - current = child; - child = current->next; - if (gtk_tree_store_clear_traverse (current, store)) - return TRUE; - } - - if (node->parent) - { - iter.stamp = store->priv->stamp; - iter.user_data = node; - - gtk_tree_store_remove (store, &iter); - } - } - else if (node->parent) - { - iter.stamp = store->priv->stamp; - iter.user_data = node; - - gtk_tree_store_remove (store, &iter); - } - - return FALSE; -} - -static void -gtk_tree_store_increment_stamp (GtkTreeStore *tree_store) -{ - GtkTreeStorePrivate *priv = tree_store->priv; - do - { - priv->stamp++; - } - while (priv->stamp == 0); -} - -/** - * gtk_tree_store_clear: - * @tree_store: a `GtkTreeStore` - * - * Removes all rows from @tree_store - **/ -void -gtk_tree_store_clear (GtkTreeStore *tree_store) -{ - g_return_if_fail (GTK_IS_TREE_STORE (tree_store)); - - gtk_tree_store_clear_traverse (tree_store->priv->root, tree_store); - gtk_tree_store_increment_stamp (tree_store); -} - -static gboolean -gtk_tree_store_iter_is_valid_helper (GtkTreeIter *iter, - GNode *first) -{ - GNode *node; - - node = first; - - do - { - if (node == iter->user_data) - return TRUE; - - if (node->children) - if (gtk_tree_store_iter_is_valid_helper (iter, node->children)) - return TRUE; - - node = node->next; - } - while (node); - - return FALSE; -} - -/** - * gtk_tree_store_iter_is_valid: - * @tree_store: a tree store - * @iter: the iterator to check - * - * Checks if the given iter is a valid iter for this `GtkTreeStore`. - * - * This function is slow. Only use it for debugging and/or testing - * purposes. - * - * Returns: %TRUE if the iter is valid, %FALSE if the iter is invalid. - **/ -gboolean -gtk_tree_store_iter_is_valid (GtkTreeStore *tree_store, - GtkTreeIter *iter) -{ - g_return_val_if_fail (GTK_IS_TREE_STORE (tree_store), FALSE); - g_return_val_if_fail (iter != NULL, FALSE); - - if (!VALID_ITER (iter, tree_store)) - return FALSE; - - return gtk_tree_store_iter_is_valid_helper (iter, tree_store->priv->root); -} - -/* DND */ - - -static gboolean real_gtk_tree_store_row_draggable (GtkTreeDragSource *drag_source, - GtkTreePath *path) -{ - return TRUE; -} - -static gboolean -gtk_tree_store_drag_data_delete (GtkTreeDragSource *drag_source, - GtkTreePath *path) -{ - GtkTreeIter iter; - - if (gtk_tree_store_get_iter (GTK_TREE_MODEL (drag_source), - &iter, - path)) - { - gtk_tree_store_remove (GTK_TREE_STORE (drag_source), - &iter); - return TRUE; - } - else - { - return FALSE; - } -} - -static GdkContentProvider * -gtk_tree_store_drag_data_get (GtkTreeDragSource *drag_source, - GtkTreePath *path) -{ - /* Note that we don't need to handle the GTK_TREE_MODEL_ROW - * target, because the default handler does it for us, but - * we do anyway for the convenience of someone maybe overriding the - * default handler. - */ - return gtk_tree_create_row_drag_content (GTK_TREE_MODEL (drag_source), path); -} - -static void -copy_node_data (GtkTreeStore *tree_store, - GtkTreeIter *src_iter, - GtkTreeIter *dest_iter) -{ - GtkTreeDataList *dl = G_NODE (src_iter->user_data)->data; - GtkTreeDataList *copy_head = NULL; - GtkTreeDataList *copy_prev = NULL; - GtkTreeDataList *copy_iter = NULL; - GtkTreePath *path; - int col; - - col = 0; - while (dl) - { - copy_iter = _gtk_tree_data_list_node_copy (dl, tree_store->priv->column_headers[col]); - - if (copy_head == NULL) - copy_head = copy_iter; - - if (copy_prev) - copy_prev->next = copy_iter; - - copy_prev = copy_iter; - - dl = dl->next; - ++col; - } - - G_NODE (dest_iter->user_data)->data = copy_head; - - path = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), dest_iter); - gtk_tree_model_row_changed (GTK_TREE_MODEL (tree_store), path, dest_iter); - gtk_tree_path_free (path); -} - -static void -recursive_node_copy (GtkTreeStore *tree_store, - GtkTreeIter *src_iter, - GtkTreeIter *dest_iter) -{ - GtkTreeIter child; - GtkTreeModel *model; - - model = GTK_TREE_MODEL (tree_store); - - copy_node_data (tree_store, src_iter, dest_iter); - - if (gtk_tree_store_iter_children (model, - &child, - src_iter)) - { - /* Need to create children and recurse. Note our - * dependence on persistent iterators here. - */ - do - { - GtkTreeIter copy; - - /* Gee, a really slow algorithm... ;-) FIXME */ - gtk_tree_store_append (tree_store, - ©, - dest_iter); - - recursive_node_copy (tree_store, &child, ©); - } - while (gtk_tree_store_iter_next (model, &child)); - } -} - -static gboolean -gtk_tree_store_drag_data_received (GtkTreeDragDest *drag_dest, - GtkTreePath *dest, - const GValue *value) -{ - GtkTreeModel *tree_model; - GtkTreeStore *tree_store; - GtkTreeModel *src_model = NULL; - GtkTreePath *src_path = NULL; - gboolean retval = FALSE; - - tree_model = GTK_TREE_MODEL (drag_dest); - tree_store = GTK_TREE_STORE (drag_dest); - - validate_tree (tree_store); - - if (gtk_tree_get_row_drag_data (value, - &src_model, - &src_path) && - src_model == tree_model) - { - /* Copy the given row to a new position */ - GtkTreeIter src_iter; - GtkTreeIter dest_iter; - GtkTreePath *prev; - - if (!gtk_tree_store_get_iter (src_model, - &src_iter, - src_path)) - { - goto out; - } - - /* Get the path to insert _after_ (dest is the path to insert _before_) */ - prev = gtk_tree_path_copy (dest); - - if (!gtk_tree_path_prev (prev)) - { - GtkTreeIter dest_parent; - GtkTreePath *parent; - GtkTreeIter *dest_parent_p; - - /* dest was the first spot at the current depth; which means - * we are supposed to prepend. - */ - - /* Get the parent, NULL if parent is the root */ - dest_parent_p = NULL; - parent = gtk_tree_path_copy (dest); - if (gtk_tree_path_up (parent) && - gtk_tree_path_get_depth (parent) > 0) - { - gtk_tree_store_get_iter (tree_model, - &dest_parent, - parent); - dest_parent_p = &dest_parent; - } - gtk_tree_path_free (parent); - parent = NULL; - - gtk_tree_store_prepend (tree_store, - &dest_iter, - dest_parent_p); - - retval = TRUE; - } - else - { - if (gtk_tree_store_get_iter (tree_model, &dest_iter, prev)) - { - GtkTreeIter tmp_iter = dest_iter; - - gtk_tree_store_insert_after (tree_store, &dest_iter, NULL, - &tmp_iter); - - retval = TRUE; - } - } - - gtk_tree_path_free (prev); - - /* If we succeeded in creating dest_iter, walk src_iter tree branch, - * duplicating it below dest_iter. - */ - - if (retval) - { - recursive_node_copy (tree_store, - &src_iter, - &dest_iter); - } - } - else - { - /* FIXME maybe add some data targets eventually, or handle text - * targets in the simple case. - */ - - } - - out: - - if (src_path) - gtk_tree_path_free (src_path); - - return retval; -} - -static gboolean -gtk_tree_store_row_drop_possible (GtkTreeDragDest *drag_dest, - GtkTreePath *dest_path, - const GValue *value) -{ - GtkTreeModel *src_model = NULL; - GtkTreePath *src_path = NULL; - GtkTreePath *tmp = NULL; - gboolean retval = FALSE; - - /* don't accept drops if the tree has been sorted */ - if (GTK_TREE_STORE_IS_SORTED (drag_dest)) - return FALSE; - - if (!gtk_tree_get_row_drag_data (value, - &src_model, - &src_path)) - goto out; - - /* can only drag to ourselves */ - if (src_model != GTK_TREE_MODEL (drag_dest)) - goto out; - - /* Can't drop into ourself. */ - if (gtk_tree_path_is_ancestor (src_path, - dest_path)) - goto out; - - /* Can't drop if dest_path's parent doesn't exist */ - { - GtkTreeIter iter; - - if (gtk_tree_path_get_depth (dest_path) > 1) - { - tmp = gtk_tree_path_copy (dest_path); - gtk_tree_path_up (tmp); - - if (!gtk_tree_store_get_iter (GTK_TREE_MODEL (drag_dest), - &iter, tmp)) - goto out; - } - } - - /* Can otherwise drop anywhere. */ - retval = TRUE; - - out: - - if (src_path) - gtk_tree_path_free (src_path); - if (tmp) - gtk_tree_path_free (tmp); - - return retval; -} - -/* Sorting and reordering */ -typedef struct _SortTuple -{ - int offset; - GNode *node; -} SortTuple; - -/* Reordering */ -static int -gtk_tree_store_reorder_func (gconstpointer a, - gconstpointer b, - gpointer user_data) -{ - SortTuple *a_reorder; - SortTuple *b_reorder; - - a_reorder = (SortTuple *)a; - b_reorder = (SortTuple *)b; - - if (a_reorder->offset < b_reorder->offset) - return -1; - if (a_reorder->offset > b_reorder->offset) - return 1; - - return 0; -} - -/** - * gtk_tree_store_reorder: (skip) - * @tree_store: A `GtkTreeStore` - * @parent: (nullable): A `GtkTreeIter` - * @new_order: (array): an array of integers mapping the new position of each child - * to its old position before the re-ordering, - * i.e. @new_order`[newpos] = oldpos`. - * - * Reorders the children of @parent in @tree_store to follow the order - * indicated by @new_order. Note that this function only works with - * unsorted stores. - */ -void -gtk_tree_store_reorder (GtkTreeStore *tree_store, - GtkTreeIter *parent, - int *new_order) -{ - int i, length = 0; - GNode *level, *node; - GtkTreePath *path; - SortTuple *sort_array; - - g_return_if_fail (GTK_IS_TREE_STORE (tree_store)); - g_return_if_fail (!GTK_TREE_STORE_IS_SORTED (tree_store)); - g_return_if_fail (parent == NULL || VALID_ITER (parent, tree_store)); - g_return_if_fail (new_order != NULL); - - if (!parent) - level = G_NODE (tree_store->priv->root)->children; - else - level = G_NODE (parent->user_data)->children; - - if (G_UNLIKELY (!level)) - { - g_warning ("%s: Cannot reorder, parent has no children", G_STRLOC); - return; - } - - /* count nodes */ - node = level; - while (node) - { - length++; - node = node->next; - } - - /* set up sortarray */ - sort_array = g_new (SortTuple, length); - - node = level; - for (i = 0; i < length; i++) - { - sort_array[new_order[i]].offset = i; - sort_array[i].node = node; - - node = node->next; - } - - g_qsort_with_data (sort_array, - length, - sizeof (SortTuple), - gtk_tree_store_reorder_func, - NULL); - - /* fix up level */ - for (i = 0; i < length - 1; i++) - { - sort_array[i].node->next = sort_array[i+1].node; - sort_array[i+1].node->prev = sort_array[i].node; - } - - sort_array[length-1].node->next = NULL; - sort_array[0].node->prev = NULL; - if (parent) - G_NODE (parent->user_data)->children = sort_array[0].node; - else - G_NODE (tree_store->priv->root)->children = sort_array[0].node; - - /* emit signal */ - if (parent) - path = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), parent); - else - path = gtk_tree_path_new (); - gtk_tree_model_rows_reordered (GTK_TREE_MODEL (tree_store), path, - parent, new_order); - gtk_tree_path_free (path); - g_free (sort_array); -} - -/** - * gtk_tree_store_swap: - * @tree_store: A `GtkTreeStore`. - * @a: A `GtkTreeIter`. - * @b: Another `GtkTreeIter`. - * - * Swaps @a and @b in the same level of @tree_store. Note that this function - * only works with unsorted stores. - **/ -void -gtk_tree_store_swap (GtkTreeStore *tree_store, - GtkTreeIter *a, - GtkTreeIter *b) -{ - GNode *tmp, *node_a, *node_b, *parent_node; - GNode *a_prev, *a_next, *b_prev, *b_next; - int i, a_count, b_count, length, *order; - GtkTreePath *path_a, *path_b; - GtkTreeIter parent; - - g_return_if_fail (GTK_IS_TREE_STORE (tree_store)); - g_return_if_fail (VALID_ITER (a, tree_store)); - g_return_if_fail (VALID_ITER (b, tree_store)); - - node_a = G_NODE (a->user_data); - node_b = G_NODE (b->user_data); - - /* basic sanity checking */ - if (node_a == node_b) - return; - - path_a = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), a); - path_b = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), b); - - g_return_if_fail (path_a && path_b); - - gtk_tree_path_up (path_a); - gtk_tree_path_up (path_b); - - if (gtk_tree_path_get_depth (path_a) == 0 - || gtk_tree_path_get_depth (path_b) == 0) - { - if (gtk_tree_path_get_depth (path_a) != gtk_tree_path_get_depth (path_b)) - { - gtk_tree_path_free (path_a); - gtk_tree_path_free (path_b); - - g_warning ("Given children are not in the same level\n"); - return; - } - parent_node = G_NODE (tree_store->priv->root); - } - else - { - if (gtk_tree_path_compare (path_a, path_b)) - { - gtk_tree_path_free (path_a); - gtk_tree_path_free (path_b); - - g_warning ("Given children don't have a common parent\n"); - return; - } - gtk_tree_store_get_iter (GTK_TREE_MODEL (tree_store), &parent, - path_a); - parent_node = G_NODE (parent.user_data); - } - gtk_tree_path_free (path_b); - - /* old links which we have to keep around */ - a_prev = node_a->prev; - a_next = node_a->next; - - b_prev = node_b->prev; - b_next = node_b->next; - - /* fix up links if the nodes are next to each other */ - if (a_prev == node_b) - a_prev = node_a; - if (a_next == node_b) - a_next = node_a; - - if (b_prev == node_a) - b_prev = node_b; - if (b_next == node_a) - b_next = node_b; - - /* counting nodes */ - tmp = parent_node->children; - i = a_count = b_count = 0; - while (tmp) - { - if (tmp == node_a) - a_count = i; - if (tmp == node_b) - b_count = i; - - tmp = tmp->next; - i++; - } - length = i; - - /* hacking the tree */ - if (!a_prev) - parent_node->children = node_b; - else - a_prev->next = node_b; - - if (a_next) - a_next->prev = node_b; - - if (!b_prev) - parent_node->children = node_a; - else - b_prev->next = node_a; - - if (b_next) - b_next->prev = node_a; - - node_a->prev = b_prev; - node_a->next = b_next; - - node_b->prev = a_prev; - node_b->next = a_next; - - /* emit signal */ - order = g_new (int, length); - for (i = 0; i < length; i++) - if (i == a_count) - order[i] = b_count; - else if (i == b_count) - order[i] = a_count; - else - order[i] = i; - - gtk_tree_model_rows_reordered (GTK_TREE_MODEL (tree_store), path_a, - parent_node == tree_store->priv->root - ? NULL : &parent, order); - gtk_tree_path_free (path_a); - g_free (order); -} - -/* WARNING: this function is *incredibly* fragile. Please smashtest after - * making changes here. - * -Kris - */ -static void -gtk_tree_store_move (GtkTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *position, - gboolean before) -{ - GNode *parent, *node, *a, *b, *tmp, *tmp_a, *tmp_b; - int old_pos, new_pos, length, i, *order; - GtkTreePath *path = NULL, *tmppath, *pos_path = NULL; - GtkTreeIter parent_iter = { 0, }; - GtkTreeIter dst_a = { 0, }; - GtkTreeIter dst_b = { 0, }; - int depth = 0; - gboolean handle_b = TRUE; - - g_return_if_fail (GTK_IS_TREE_STORE (tree_store)); - g_return_if_fail (!GTK_TREE_STORE_IS_SORTED (tree_store)); - g_return_if_fail (VALID_ITER (iter, tree_store)); - if (position) - g_return_if_fail (VALID_ITER (position, tree_store)); - - a = b = NULL; - - /* sanity checks */ - if (position) - { - path = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter); - pos_path = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), - position); - - /* if before: - * moving the iter before path or "path + 1" doesn't make sense - * else - * moving the iter before path or "path - 1" doesn't make sense - */ - if (!gtk_tree_path_compare (path, pos_path)) - goto free_paths_and_out; - - if (before) - gtk_tree_path_next (path); - else - gtk_tree_path_prev (path); - - if (!gtk_tree_path_compare (path, pos_path)) - goto free_paths_and_out; - - if (before) - gtk_tree_path_prev (path); - else - gtk_tree_path_next (path); - - if (gtk_tree_path_get_depth (path) != gtk_tree_path_get_depth (pos_path)) - { - g_warning ("Given children are not in the same level\n"); - - goto free_paths_and_out; - } - - tmppath = gtk_tree_path_copy (pos_path); - gtk_tree_path_up (path); - gtk_tree_path_up (tmppath); - - if (gtk_tree_path_get_depth (path) > 0 && - gtk_tree_path_compare (path, tmppath)) - { - g_warning ("Given children are not in the same level\n"); - - gtk_tree_path_free (tmppath); - goto free_paths_and_out; - } - - gtk_tree_path_free (tmppath); - } - - if (!path) - { - path = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter); - gtk_tree_path_up (path); - } - - depth = gtk_tree_path_get_depth (path); - - if (depth) - { - gtk_tree_store_get_iter (GTK_TREE_MODEL (tree_store), - &parent_iter, path); - - parent = G_NODE (parent_iter.user_data); - } - else - parent = G_NODE (tree_store->priv->root); - - /* yes, I know that this can be done shorter, but I'm doing it this way - * so the code is also maintainable - */ - - if (before && position) - { - b = G_NODE (position->user_data); - - if (gtk_tree_path_get_indices (pos_path)[gtk_tree_path_get_depth (pos_path) - 1] > 0) - { - gtk_tree_path_prev (pos_path); - if (gtk_tree_store_get_iter (GTK_TREE_MODEL (tree_store), - &dst_a, pos_path)) - a = G_NODE (dst_a.user_data); - else - a = NULL; - gtk_tree_path_next (pos_path); - } - - /* if b is NULL, a is NULL too -- we are at the beginning of the list - * yes and we leak memory here ... - */ - g_return_if_fail (b); - } - else if (before && !position) - { - /* move before without position is appending */ - a = NULL; - b = NULL; - } - else /* !before */ - { - if (position) - a = G_NODE (position->user_data); - else - a = NULL; - - if (position) - { - gtk_tree_path_next (pos_path); - if (gtk_tree_store_get_iter (GTK_TREE_MODEL (tree_store), &dst_b, pos_path)) - b = G_NODE (dst_b.user_data); - else - b = NULL; - gtk_tree_path_prev (pos_path); - } - else - { - /* move after without position is prepending */ - if (depth) - gtk_tree_store_iter_children (GTK_TREE_MODEL (tree_store), &dst_b, - &parent_iter); - else - gtk_tree_store_iter_children (GTK_TREE_MODEL (tree_store), &dst_b, - NULL); - - b = G_NODE (dst_b.user_data); - } - - /* if a is NULL, b is NULL too -- we are at the end of the list - * yes and we leak memory here ... - */ - if (position) - g_return_if_fail (a); - } - - /* counting nodes */ - tmp = parent->children; - - length = old_pos = 0; - while (tmp) - { - if (tmp == iter->user_data) - old_pos = length; - - tmp = tmp->next; - length++; - } - - /* remove node from list */ - node = G_NODE (iter->user_data); - tmp_a = node->prev; - tmp_b = node->next; - - if (tmp_a) - tmp_a->next = tmp_b; - else - parent->children = tmp_b; - - if (tmp_b) - tmp_b->prev = tmp_a; - - /* and reinsert the node */ - if (a) - { - tmp = a->next; - - a->next = node; - node->next = tmp; - node->prev = a; - } - else if (!a && !before) - { - tmp = parent->children; - - node->prev = NULL; - parent->children = node; - - node->next = tmp; - if (tmp) - tmp->prev = node; - - handle_b = FALSE; - } - else if (!a && before) - { - if (!position) - { - node->parent = NULL; - node->next = node->prev = NULL; - - /* before with sibling = NULL appends */ - g_node_insert_before (parent, NULL, node); - } - else - { - node->parent = NULL; - node->next = node->prev = NULL; - - /* after with sibling = NULL prepends */ - g_node_insert_after (parent, NULL, node); - } - - handle_b = FALSE; - } - - if (handle_b) - { - if (b) - { - tmp = b->prev; - - b->prev = node; - node->prev = tmp; - node->next = b; - } - else if (!(!a && before)) /* !a && before is completely handled above */ - node->next = NULL; - } - - /* emit signal */ - if (position) - new_pos = gtk_tree_path_get_indices (pos_path)[gtk_tree_path_get_depth (pos_path)-1]; - else if (before) - { - if (depth) - new_pos = gtk_tree_store_iter_n_children (GTK_TREE_MODEL (tree_store), - &parent_iter) - 1; - else - new_pos = gtk_tree_store_iter_n_children (GTK_TREE_MODEL (tree_store), - NULL) - 1; - } - else - new_pos = 0; - - if (new_pos > old_pos) - { - if (before && position) - new_pos--; - } - else - { - if (!before && position) - new_pos++; - } - - order = g_new (int, length); - if (new_pos > old_pos) - { - for (i = 0; i < length; i++) - if (i < old_pos) - order[i] = i; - else if (i >= old_pos && i < new_pos) - order[i] = i + 1; - else if (i == new_pos) - order[i] = old_pos; - else - order[i] = i; - } - else - { - for (i = 0; i < length; i++) - if (i == new_pos) - order[i] = old_pos; - else if (i > new_pos && i <= old_pos) - order[i] = i - 1; - else - order[i] = i; - } - - if (depth) - { - tmppath = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), - &parent_iter); - gtk_tree_model_rows_reordered (GTK_TREE_MODEL (tree_store), - tmppath, &parent_iter, order); - } - else - { - tmppath = gtk_tree_path_new (); - gtk_tree_model_rows_reordered (GTK_TREE_MODEL (tree_store), - tmppath, NULL, order); - } - - gtk_tree_path_free (tmppath); - gtk_tree_path_free (path); - if (position) - gtk_tree_path_free (pos_path); - g_free (order); - - return; - -free_paths_and_out: - gtk_tree_path_free (path); - gtk_tree_path_free (pos_path); -} - -/** - * gtk_tree_store_move_before: - * @tree_store: A `GtkTreeStore` - * @iter: A `GtkTreeIter` - * @position: (nullable): A `GtkTreeIter` - * - * Moves @iter in @tree_store to the position before @position. @iter and - * @position should be in the same level. Note that this function only - * works with unsorted stores. If @position is %NULL, @iter will be - * moved to the end of the level. - **/ -void -gtk_tree_store_move_before (GtkTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *position) -{ - gtk_tree_store_move (tree_store, iter, position, TRUE); -} - -/** - * gtk_tree_store_move_after: - * @tree_store: A `GtkTreeStore` - * @iter: A `GtkTreeIter`. - * @position: (nullable): A `GtkTreeIter`. - * - * Moves @iter in @tree_store to the position after @position. @iter and - * @position should be in the same level. Note that this function only - * works with unsorted stores. If @position is %NULL, @iter will be moved - * to the start of the level. - **/ -void -gtk_tree_store_move_after (GtkTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *position) -{ - gtk_tree_store_move (tree_store, iter, position, FALSE); -} - -/* Sorting */ -static int -gtk_tree_store_compare_func (gconstpointer a, - gconstpointer b, - gpointer user_data) -{ - GtkTreeStore *tree_store = user_data; - GtkTreeStorePrivate *priv = tree_store->priv; - GNode *node_a; - GNode *node_b; - GtkTreeIterCompareFunc func; - gpointer data; - - GtkTreeIter iter_a; - GtkTreeIter iter_b; - int retval; - - if (priv->sort_column_id != -1) - { - GtkTreeDataSortHeader *header; - - header = _gtk_tree_data_list_get_header (priv->sort_list, - priv->sort_column_id); - g_return_val_if_fail (header != NULL, 0); - g_return_val_if_fail (header->func != NULL, 0); - - func = header->func; - data = header->data; - } - else - { - g_return_val_if_fail (priv->default_sort_func != NULL, 0); - func = priv->default_sort_func; - data = priv->default_sort_data; - } - - node_a = ((SortTuple *) a)->node; - node_b = ((SortTuple *) b)->node; - - iter_a.stamp = priv->stamp; - iter_a.user_data = node_a; - iter_b.stamp = priv->stamp; - iter_b.user_data = node_b; - - retval = (* func) (GTK_TREE_MODEL (user_data), &iter_a, &iter_b, data); - - if (priv->order == GTK_SORT_DESCENDING) - { - if (retval > 0) - retval = -1; - else if (retval < 0) - retval = 1; - } - return retval; -} - -static void -gtk_tree_store_sort_helper (GtkTreeStore *tree_store, - GNode *parent, - gboolean recurse) -{ - GtkTreeIter iter; - GArray *sort_array; - GNode *node; - GNode *tmp_node; - int list_length; - int i; - int *new_order; - GtkTreePath *path; - - node = parent->children; - if (node == NULL || node->next == NULL) - { - if (recurse && node && node->children) - gtk_tree_store_sort_helper (tree_store, node, TRUE); - - return; - } - - list_length = 0; - for (tmp_node = node; tmp_node; tmp_node = tmp_node->next) - list_length++; - - sort_array = g_array_sized_new (FALSE, FALSE, sizeof (SortTuple), list_length); - - i = 0; - for (tmp_node = node; tmp_node; tmp_node = tmp_node->next) - { - SortTuple tuple; - - tuple.offset = i; - tuple.node = tmp_node; - g_array_append_val (sort_array, tuple); - i++; - } - - /* Sort the array */ - g_array_sort_with_data (sort_array, gtk_tree_store_compare_func, tree_store); - - for (i = 0; i < list_length - 1; i++) - { - g_array_index (sort_array, SortTuple, i).node->next = - g_array_index (sort_array, SortTuple, i + 1).node; - g_array_index (sort_array, SortTuple, i + 1).node->prev = - g_array_index (sort_array, SortTuple, i).node; - } - g_array_index (sort_array, SortTuple, list_length - 1).node->next = NULL; - g_array_index (sort_array, SortTuple, 0).node->prev = NULL; - parent->children = g_array_index (sort_array, SortTuple, 0).node; - - /* Let the world know about our new order */ - new_order = g_new (int, list_length); - for (i = 0; i < list_length; i++) - new_order[i] = g_array_index (sort_array, SortTuple, i).offset; - - iter.stamp = tree_store->priv->stamp; - iter.user_data = parent; - path = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), &iter); - gtk_tree_model_rows_reordered (GTK_TREE_MODEL (tree_store), - path, &iter, new_order); - gtk_tree_path_free (path); - g_free (new_order); - g_array_free (sort_array, TRUE); - - if (recurse) - { - for (tmp_node = parent->children; tmp_node; tmp_node = tmp_node->next) - { - if (tmp_node->children) - gtk_tree_store_sort_helper (tree_store, tmp_node, TRUE); - } - } -} - -static void -gtk_tree_store_sort (GtkTreeStore *tree_store) -{ - GtkTreeStorePrivate *priv = tree_store->priv; - - if (!GTK_TREE_STORE_IS_SORTED (tree_store)) - return; - - if (priv->sort_column_id != -1) - { - GtkTreeDataSortHeader *header = NULL; - - header = _gtk_tree_data_list_get_header (priv->sort_list, - priv->sort_column_id); - - /* We want to make sure that we have a function */ - g_return_if_fail (header != NULL); - g_return_if_fail (header->func != NULL); - } - else - { - g_return_if_fail (priv->default_sort_func != NULL); - } - - gtk_tree_store_sort_helper (tree_store, G_NODE (priv->root), TRUE); -} - -static void -gtk_tree_store_sort_iter_changed (GtkTreeStore *tree_store, - GtkTreeIter *iter, - int column, - gboolean emit_signal) -{ - GtkTreeStorePrivate *priv = tree_store->priv; - GNode *prev = NULL; - GNode *next = NULL; - GNode *node; - GtkTreePath *tmp_path; - GtkTreeIter tmp_iter; - int cmp_a = 0; - int cmp_b = 0; - int i; - int old_location; - int new_location; - int *new_order; - int length; - GtkTreeIterCompareFunc func; - gpointer data; - - g_return_if_fail (G_NODE (iter->user_data)->parent != NULL); - - tmp_iter.stamp = priv->stamp; - if (priv->sort_column_id != -1) - { - GtkTreeDataSortHeader *header; - header = _gtk_tree_data_list_get_header (priv->sort_list, - priv->sort_column_id); - g_return_if_fail (header != NULL); - g_return_if_fail (header->func != NULL); - func = header->func; - data = header->data; - } - else - { - g_return_if_fail (priv->default_sort_func != NULL); - func = priv->default_sort_func; - data = priv->default_sort_data; - } - - /* If it's the built in function, we don't sort. */ - if (func == _gtk_tree_data_list_compare_func && - priv->sort_column_id != column) - return; - - old_location = 0; - node = G_NODE (iter->user_data)->parent->children; - /* First we find the iter, its prev, and its next */ - while (node) - { - if (node == G_NODE (iter->user_data)) - break; - old_location++; - node = node->next; - } - g_assert (node != NULL); - - prev = node->prev; - next = node->next; - - /* Check the common case, where we don't need to sort it moved. */ - if (prev != NULL) - { - tmp_iter.user_data = prev; - cmp_a = (* func) (GTK_TREE_MODEL (tree_store), &tmp_iter, iter, data); - } - - if (next != NULL) - { - tmp_iter.user_data = next; - cmp_b = (* func) (GTK_TREE_MODEL (tree_store), iter, &tmp_iter, data); - } - - if (priv->order == GTK_SORT_DESCENDING) - { - if (cmp_a < 0) - cmp_a = 1; - else if (cmp_a > 0) - cmp_a = -1; - - if (cmp_b < 0) - cmp_b = 1; - else if (cmp_b > 0) - cmp_b = -1; - } - - if (prev == NULL && cmp_b <= 0) - return; - else if (next == NULL && cmp_a <= 0) - return; - else if (prev != NULL && next != NULL && - cmp_a <= 0 && cmp_b <= 0) - return; - - /* We actually need to sort it */ - /* First, remove the old link. */ - - if (prev) - prev->next = next; - else - node->parent->children = next; - - if (next) - next->prev = prev; - - node->prev = NULL; - node->next = NULL; - - /* FIXME: as an optimization, we can potentially start at next */ - prev = NULL; - node = node->parent->children; - new_location = 0; - tmp_iter.user_data = node; - if (priv->order == GTK_SORT_DESCENDING) - cmp_a = (* func) (GTK_TREE_MODEL (tree_store), &tmp_iter, iter, data); - else - cmp_a = (* func) (GTK_TREE_MODEL (tree_store), iter, &tmp_iter, data); - - while ((node->next) && (cmp_a > 0)) - { - prev = node; - node = node->next; - new_location++; - tmp_iter.user_data = node; - if (priv->order == GTK_SORT_DESCENDING) - cmp_a = (* func) (GTK_TREE_MODEL (tree_store), &tmp_iter, iter, data); - else - cmp_a = (* func) (GTK_TREE_MODEL (tree_store), iter, &tmp_iter, data); - } - - if ((!node->next) && (cmp_a > 0)) - { - new_location++; - node->next = G_NODE (iter->user_data); - node->next->prev = node; - } - else if (prev) - { - prev->next = G_NODE (iter->user_data); - prev->next->prev = prev; - G_NODE (iter->user_data)->next = node; - G_NODE (iter->user_data)->next->prev = G_NODE (iter->user_data); - } - else - { - G_NODE (iter->user_data)->next = G_NODE (iter->user_data)->parent->children; - G_NODE (iter->user_data)->next->prev = G_NODE (iter->user_data); - G_NODE (iter->user_data)->parent->children = G_NODE (iter->user_data); - } - - if (!emit_signal) - return; - - /* Emit the reordered signal. */ - length = g_node_n_children (node->parent); - new_order = g_new (int, length); - if (old_location < new_location) - for (i = 0; i < length; i++) - { - if (i < old_location || - i > new_location) - new_order[i] = i; - else if (i >= old_location && - i < new_location) - new_order[i] = i + 1; - else if (i == new_location) - new_order[i] = old_location; - } - else - for (i = 0; i < length; i++) - { - if (i < new_location || - i > old_location) - new_order[i] = i; - else if (i > new_location && - i <= old_location) - new_order[i] = i - 1; - else if (i == new_location) - new_order[i] = old_location; - } - - tmp_iter.user_data = node->parent; - tmp_path = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), &tmp_iter); - - gtk_tree_model_rows_reordered (GTK_TREE_MODEL (tree_store), - tmp_path, &tmp_iter, - new_order); - - gtk_tree_path_free (tmp_path); - g_free (new_order); -} - - -static gboolean -gtk_tree_store_get_sort_column_id (GtkTreeSortable *sortable, - int *sort_column_id, - GtkSortType *order) -{ - GtkTreeStore *tree_store = (GtkTreeStore *) sortable; - GtkTreeStorePrivate *priv = tree_store->priv; - - if (sort_column_id) - * sort_column_id = priv->sort_column_id; - if (order) - * order = priv->order; - - if (priv->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID || - priv->sort_column_id == GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID) - return FALSE; - - return TRUE; -} - -static void -gtk_tree_store_set_sort_column_id (GtkTreeSortable *sortable, - int sort_column_id, - GtkSortType order) -{ - GtkTreeStore *tree_store = (GtkTreeStore *) sortable; - GtkTreeStorePrivate *priv = tree_store->priv; - - if ((priv->sort_column_id == sort_column_id) && - (priv->order == order)) - return; - - if (sort_column_id != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID) - { - if (sort_column_id != GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID) - { - GtkTreeDataSortHeader *header = NULL; - - header = _gtk_tree_data_list_get_header (priv->sort_list, - sort_column_id); - - /* We want to make sure that we have a function */ - g_return_if_fail (header != NULL); - g_return_if_fail (header->func != NULL); - } - else - { - g_return_if_fail (priv->default_sort_func != NULL); - } - } - - priv->sort_column_id = sort_column_id; - priv->order = order; - - gtk_tree_sortable_sort_column_changed (sortable); - - gtk_tree_store_sort (tree_store); -} - -static void -gtk_tree_store_set_sort_func (GtkTreeSortable *sortable, - int sort_column_id, - GtkTreeIterCompareFunc func, - gpointer data, - GDestroyNotify destroy) -{ - GtkTreeStore *tree_store = (GtkTreeStore *) sortable; - GtkTreeStorePrivate *priv = tree_store->priv; - - priv->sort_list = _gtk_tree_data_list_set_header (priv->sort_list, - sort_column_id, - func, data, destroy); - - if (priv->sort_column_id == sort_column_id) - gtk_tree_store_sort (tree_store); -} - -static void -gtk_tree_store_set_default_sort_func (GtkTreeSortable *sortable, - GtkTreeIterCompareFunc func, - gpointer data, - GDestroyNotify destroy) -{ - GtkTreeStore *tree_store = (GtkTreeStore *) sortable; - GtkTreeStorePrivate *priv = tree_store->priv; - - if (priv->default_sort_destroy) - { - GDestroyNotify d = priv->default_sort_destroy; - - priv->default_sort_destroy = NULL; - d (priv->default_sort_data); - } - - priv->default_sort_func = func; - priv->default_sort_data = data; - priv->default_sort_destroy = destroy; - - if (priv->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID) - gtk_tree_store_sort (tree_store); -} - -static gboolean -gtk_tree_store_has_default_sort_func (GtkTreeSortable *sortable) -{ - GtkTreeStore *tree_store = (GtkTreeStore *) sortable; - - return (tree_store->priv->default_sort_func != NULL); -} - -#ifdef G_ENABLE_DEBUG -static void -validate_gnode (GNode* node) -{ - GNode *iter; - - iter = node->children; - while (iter != NULL) - { - g_assert (iter->parent == node); - if (iter->prev) - g_assert (iter->prev->next == iter); - validate_gnode (iter); - iter = iter->next; - } -} -#endif - -/* GtkBuildable custom tag implementation - * - * - * - * - * - */ -typedef struct { - gboolean translatable; - char *context; - int id; -} ColInfo; - -typedef struct { - GtkBuilder *builder; - GObject *object; - GSList *column_type_names; - GType *column_types; - GValue *values; - int *colids; - ColInfo **columns; - int last_row; - int n_columns; - int row_column; - gboolean is_data; - const char *domain; - GList *parents; - gboolean row_is_open; -} SubParserData; - -static void -append_current_row (SubParserData *data) -{ - GtkTreeIter *parent; - GtkTreeIter iter; - int i; - - if (data->parents) - parent = data->parents->data; - else - parent = NULL; - - gtk_tree_store_insert_with_valuesv (GTK_TREE_STORE (data->object), - &iter, - parent, - -1, - data->colids, - data->values, - data->row_column); - - data->parents = g_list_prepend (data->parents, gtk_tree_iter_copy (&iter)); - - for (i = 0; i < data->row_column; i++) - { - ColInfo *info = data->columns[i]; - g_free (info->context); - g_slice_free (ColInfo, info); - data->columns[i] = NULL; - g_value_unset (&data->values[i]); - } - g_free (data->values); - data->values = g_new0 (GValue, data->n_columns); - data->last_row++; - data->row_column = 0; - data->row_is_open = FALSE; -} - -static void -tree_store_start_element (GtkBuildableParseContext *context, - const char *element_name, - const char **names, - const char **values, - gpointer user_data, - GError **error) -{ - SubParserData *data = (SubParserData*)user_data; - - if (strcmp (element_name, "col") == 0) - { - int id = -1; - const char *id_str; - const char *msg_context = NULL; - gboolean translatable = FALSE; - ColInfo *info; - GValue val = G_VALUE_INIT; - - if (!_gtk_builder_check_parent (data->builder, context, "row", error)) - return; - - if (!data->row_is_open) - { - g_set_error (error, - GTK_BUILDER_ERROR, - GTK_BUILDER_ERROR_INVALID_TAG, - "Can't use here"); - _gtk_builder_prefix_error (data->builder, context, error); - return; - } - - if (data->row_column >= data->n_columns) - { - g_set_error (error, - GTK_BUILDER_ERROR, GTK_BUILDER_ERROR_INVALID_VALUE, - "Too many columns, maximum is %d", data->n_columns - 1); - _gtk_builder_prefix_error (data->builder, context, error); - return; - } - - if (!g_markup_collect_attributes (element_name, names, values, error, - G_MARKUP_COLLECT_STRING, "id", &id_str, - G_MARKUP_COLLECT_BOOLEAN|G_MARKUP_COLLECT_OPTIONAL, "translatable", &translatable, - G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "comments", NULL, - G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "context", &msg_context, - G_MARKUP_COLLECT_INVALID)) - { - _gtk_builder_prefix_error (data->builder, context, error); - return; - } - - if (!gtk_builder_value_from_string_type (data->builder, G_TYPE_INT, id_str, &val, error)) - { - _gtk_builder_prefix_error (data->builder, context, error); - return; - } - - id = g_value_get_int (&val); - if (id < 0 || id >= data->n_columns) - { - g_set_error (error, - GTK_BUILDER_ERROR, GTK_BUILDER_ERROR_INVALID_VALUE, - "id value %d out of range", id); - _gtk_builder_prefix_error (data->builder, context, error); - return; - } - - info = g_slice_new0 (ColInfo); - info->translatable = translatable; - info->context = g_strdup (msg_context); - info->id = id; - - data->colids[data->row_column] = id; - data->columns[data->row_column] = info; - data->row_column++; - data->is_data = TRUE; - } - else if (strcmp (element_name, "row") == 0) - { - if (!_gtk_builder_check_parents (data->builder, context, error, "data", "row", NULL)) - return; - - if (data->row_is_open) - append_current_row (data); - - if (!g_markup_collect_attributes (element_name, names, values, error, - G_MARKUP_COLLECT_INVALID, NULL, NULL, - G_MARKUP_COLLECT_INVALID)) - _gtk_builder_prefix_error (data->builder, context, error); - - data->row_is_open = TRUE; - } - else if (strcmp (element_name, "columns") == 0 || - strcmp (element_name, "data") == 0) - { - if (!_gtk_builder_check_parent (data->builder, context, "object", error)) - return; - - if (!g_markup_collect_attributes (element_name, names, values, error, - G_MARKUP_COLLECT_INVALID, NULL, NULL, - G_MARKUP_COLLECT_INVALID)) - _gtk_builder_prefix_error (data->builder, context, error); - } - else if (strcmp (element_name, "column") == 0) - { - const char *type; - - if (!_gtk_builder_check_parent (data->builder, context, "columns", error)) - return; - - if (!g_markup_collect_attributes (element_name, names, values, error, - G_MARKUP_COLLECT_STRING, "type", &type, - G_MARKUP_COLLECT_INVALID)) - { - _gtk_builder_prefix_error (data->builder, context, error); - return; - } - - data->column_type_names = g_slist_prepend (data->column_type_names, g_strdup (type)); - } - - else - { - _gtk_builder_error_unhandled_tag (data->builder, context, - "GtkTreeStore", element_name, - error); - } -} - -static void -tree_store_end_element (GtkBuildableParseContext *context, - const char *element_name, - gpointer user_data, - GError **error) -{ - SubParserData *data = (SubParserData*)user_data; - - g_assert(data->builder); - - if (strcmp (element_name, "row") == 0) - { - GtkTreeIter *parent; - - if (data->row_column > 0) - append_current_row (data); - - parent = data->parents->data; - data->parents = g_list_remove (data->parents, parent); - gtk_tree_iter_free (parent); - } - else if (strcmp (element_name, "columns") == 0) - { - GType *column_types; - GSList *l; - int i; - GType type; - - data = (SubParserData*)user_data; - data->column_type_names = g_slist_reverse (data->column_type_names); - column_types = g_new0 (GType, g_slist_length (data->column_type_names)); - - for (l = data->column_type_names, i = 0; l; l = l->next, i++) - { - type = gtk_builder_get_type_from_name (data->builder, l->data); - if (type == G_TYPE_INVALID) - { - g_warning ("Unknown type %s specified in treemodel %s", - (const char *)l->data, - gtk_buildable_get_buildable_id (GTK_BUILDABLE (data->object))); - continue; - } - column_types[i] = type; - - g_free (l->data); - } - - gtk_tree_store_set_column_types (GTK_TREE_STORE (data->object), i, column_types); - - g_free (column_types); - } - else if (strcmp (element_name, "col") == 0) - { - data->is_data = FALSE; - } -} - -static void -tree_store_text (GtkBuildableParseContext *context, - const char *text, - gsize text_len, - gpointer user_data, - GError **error) -{ - SubParserData *data = (SubParserData*)user_data; - int i; - char *string; - ColInfo *info; - - if (!data->is_data) - return; - - i = data->row_column - 1; - info = data->columns[i]; - - string = g_strndup (text, text_len); - if (info->translatable && text_len) - { - char *translated; - - /* FIXME: This will not use the domain set in the .ui file, - * since the parser is not telling the builder about the domain. - * However, it will work for gtk_builder_set_translation_domain() calls. - */ - translated = g_strdup (_gtk_builder_parser_translate (data->domain, - info->context, - string)); - g_free (string); - string = translated; - } - - if (!gtk_builder_value_from_string_type (data->builder, - data->column_types[info->id], - string, - &data->values[i], - error)) - { - _gtk_builder_prefix_error (data->builder, context, error); - } - g_free (string); -} - -static const GtkBuildableParser tree_store_parser = -{ - tree_store_start_element, - tree_store_end_element, - tree_store_text -}; - -static gboolean -gtk_tree_store_buildable_custom_tag_start (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const char *tagname, - GtkBuildableParser *parser, - gpointer *parser_data) -{ - SubParserData *data; - - if (child) - return FALSE; - - if (strcmp (tagname, "columns") == 0) - { - data = g_slice_new0 (SubParserData); - data->builder = builder; - data->column_type_names = NULL; - data->object = G_OBJECT (buildable); - - *parser = tree_store_parser; - *parser_data = data; - - return TRUE; - } - else if (strcmp (tagname, "data") == 0) - { - int n_columns = gtk_tree_store_get_n_columns (GTK_TREE_MODEL (buildable)); - if (n_columns == 0) - g_error ("Cannot append data to an empty model"); - - data = g_slice_new0 (SubParserData); - data->builder = builder; - data->object = G_OBJECT (buildable); - data->values = g_new0 (GValue, n_columns); - data->colids = g_new0 (int, n_columns); - data->columns = g_new0 (ColInfo *, n_columns); - data->column_types = GTK_TREE_STORE (buildable)->priv->column_headers; - data->n_columns = n_columns; - data->last_row = 0; - data->domain = gtk_builder_get_translation_domain (builder); - - *parser = tree_store_parser; - *parser_data = data; - - return TRUE; - } - - return FALSE; -} - -static void -gtk_tree_store_buildable_custom_tag_end (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const char *tagname, - gpointer parser_data) -{ - SubParserData *data = (SubParserData*)parser_data; - - if (strcmp (tagname, "columns") == 0) - { - g_slist_free (data->column_type_names); - g_slice_free (SubParserData, data); - } - else if (strcmp (tagname, "data") == 0) - { - int i; - for (i = 0; i < data->n_columns; i++) - { - ColInfo *info = data->columns[i]; - if (info) - { - g_free (info->context); - g_slice_free (ColInfo, info); - } - } - g_free (data->colids); - g_free (data->columns); - g_free (data->values); - g_slice_free (SubParserData, data); - } -} diff --git a/gtk/gtktreestore.h b/gtk/gtktreestore.h deleted file mode 100644 index 59709344ee..0000000000 --- a/gtk/gtktreestore.h +++ /dev/null @@ -1,169 +0,0 @@ -/* gtktreestore.h - * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#ifndef __GTK_TREE_STORE_H__ -#define __GTK_TREE_STORE_H__ - -#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) -#error "Only can be included directly." -#endif - -#include -#include -#include -#include - - -G_BEGIN_DECLS - - -#define GTK_TYPE_TREE_STORE (gtk_tree_store_get_type ()) -#define GTK_TREE_STORE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_TREE_STORE, GtkTreeStore)) -#define GTK_TREE_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_TREE_STORE, GtkTreeStoreClass)) -#define GTK_IS_TREE_STORE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_TREE_STORE)) -#define GTK_IS_TREE_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_TREE_STORE)) -#define GTK_TREE_STORE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_TREE_STORE, GtkTreeStoreClass)) - -typedef struct _GtkTreeStore GtkTreeStore; -typedef struct _GtkTreeStoreClass GtkTreeStoreClass; -typedef struct _GtkTreeStorePrivate GtkTreeStorePrivate; - -struct _GtkTreeStore -{ - GObject parent; - - GtkTreeStorePrivate *priv; -}; - -struct _GtkTreeStoreClass -{ - GObjectClass parent_class; - - /*< private >*/ - gpointer padding[8]; -}; - - -GDK_AVAILABLE_IN_ALL -GType gtk_tree_store_get_type (void) G_GNUC_CONST; -GDK_AVAILABLE_IN_ALL -GtkTreeStore *gtk_tree_store_new (int n_columns, - ...); -GDK_AVAILABLE_IN_ALL -GtkTreeStore *gtk_tree_store_newv (int n_columns, - GType *types); -GDK_AVAILABLE_IN_ALL -void gtk_tree_store_set_column_types (GtkTreeStore *tree_store, - int n_columns, - GType *types); - -/* NOTE: use gtk_tree_model_get to get values from a GtkTreeStore */ - -GDK_AVAILABLE_IN_ALL -void gtk_tree_store_set_value (GtkTreeStore *tree_store, - GtkTreeIter *iter, - int column, - GValue *value); -GDK_AVAILABLE_IN_ALL -void gtk_tree_store_set (GtkTreeStore *tree_store, - GtkTreeIter *iter, - ...); -GDK_AVAILABLE_IN_ALL -void gtk_tree_store_set_valuesv (GtkTreeStore *tree_store, - GtkTreeIter *iter, - int *columns, - GValue *values, - int n_values); -GDK_AVAILABLE_IN_ALL -void gtk_tree_store_set_valist (GtkTreeStore *tree_store, - GtkTreeIter *iter, - va_list var_args); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_store_remove (GtkTreeStore *tree_store, - GtkTreeIter *iter); -GDK_AVAILABLE_IN_ALL -void gtk_tree_store_insert (GtkTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *parent, - int position); -GDK_AVAILABLE_IN_ALL -void gtk_tree_store_insert_before (GtkTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *parent, - GtkTreeIter *sibling); -GDK_AVAILABLE_IN_ALL -void gtk_tree_store_insert_after (GtkTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *parent, - GtkTreeIter *sibling); -GDK_AVAILABLE_IN_ALL -void gtk_tree_store_insert_with_values (GtkTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *parent, - int position, - ...); -GDK_AVAILABLE_IN_ALL -void gtk_tree_store_insert_with_valuesv (GtkTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *parent, - int position, - int *columns, - GValue *values, - int n_values); -GDK_AVAILABLE_IN_ALL -void gtk_tree_store_prepend (GtkTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *parent); -GDK_AVAILABLE_IN_ALL -void gtk_tree_store_append (GtkTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *parent); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_store_is_ancestor (GtkTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *descendant); -GDK_AVAILABLE_IN_ALL -int gtk_tree_store_iter_depth (GtkTreeStore *tree_store, - GtkTreeIter *iter); -GDK_AVAILABLE_IN_ALL -void gtk_tree_store_clear (GtkTreeStore *tree_store); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_store_iter_is_valid (GtkTreeStore *tree_store, - GtkTreeIter *iter); -GDK_AVAILABLE_IN_ALL -void gtk_tree_store_reorder (GtkTreeStore *tree_store, - GtkTreeIter *parent, - int *new_order); -GDK_AVAILABLE_IN_ALL -void gtk_tree_store_swap (GtkTreeStore *tree_store, - GtkTreeIter *a, - GtkTreeIter *b); -GDK_AVAILABLE_IN_ALL -void gtk_tree_store_move_before (GtkTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *position); -GDK_AVAILABLE_IN_ALL -void gtk_tree_store_move_after (GtkTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *position); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkTreeStore, g_object_unref) - -G_END_DECLS - - -#endif /* __GTK_TREE_STORE_H__ */ diff --git a/gtk/gtktreeview.c b/gtk/gtktreeview.c deleted file mode 100644 index b55da32e6f..0000000000 --- a/gtk/gtktreeview.c +++ /dev/null @@ -1,14862 +0,0 @@ -/* gtktreeview.c - * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - - -#include "config.h" - -#include "gtktreeview.h" - -#include "gtkadjustmentprivate.h" -#include "gtkbox.h" -#include "gtkbuildable.h" -#include "gtkbutton.h" -#include "gtkcelllayout.h" -#include "gtkcellrenderer.h" -#include "gtkcssnumbervalueprivate.h" -#include "gtkcsscolorvalueprivate.h" -#include "gtkdragsourceprivate.h" -#include "gtkdragicon.h" -#include "gtkdroptargetasync.h" -#include "gtkentryprivate.h" -#include "gtksearchentryprivate.h" -#include "gtkeventcontrollerkey.h" -#include "gtkeventcontrollerfocus.h" -#include "gtkeventcontrollermotion.h" -#include "gtkeventcontrollerscroll.h" -#include "gtkframe.h" -#include "gtkgesturedrag.h" -#include "gtkgestureclick.h" -#include "gtkgesturesingle.h" -#include "gtklabel.h" -#include "gtkmain.h" -#include "gtkmarshalers.h" -#include "gtkprivate.h" -#include "gtktext.h" -#include "gtktreerbtreeprivate.h" -#include "gtkrendericonprivate.h" -#include "gtkscrollable.h" -#include "gtksettingsprivate.h" -#include "gtkshortcutcontroller.h" -#include "gtksnapshot.h" -#include "gtkstylecontextprivate.h" -#include "gtktooltip.h" -#include "gtktreednd.h" -#include "gtktreemodelsort.h" -#include "gtktreeprivate.h" -#include "gtktypebuiltins.h" -#include "gtkwidgetprivate.h" -#include "gtkwindowgroup.h" -#include "gtknative.h" -#include "gtkpopover.h" - -#include "gdk/gdkeventsprivate.h" -#include "gdk/gdktextureprivate.h" - -#include -#include - -/** - * GtkTreeView: - * - * A widget for displaying both trees and lists - * - * Widget that displays any object that implements the [iface@Gtk.TreeModel] interface. - * - * Please refer to the [tree widget conceptual overview](section-tree-widget.html) - * for an overview of all the objects and data types related to the tree - * widget and how they work together. - * - * ## Coordinate systems in GtkTreeView API - * - * Several different coordinate systems are exposed in the `GtkTreeView` API. - * These are: - * - * ![](tree-view-coordinates.png) - * - * - Widget coordinates: Coordinates relative to the widget (usually `widget->window`). - * - * - Bin window coordinates: Coordinates relative to the window that GtkTreeView renders to. - * - * - Tree coordinates: Coordinates relative to the entire scrollable area of GtkTreeView. These - * coordinates start at (0, 0) for row 0 of the tree. - * - * Several functions are available for converting between the different - * coordinate systems. The most common translations are between widget and bin - * window coordinates and between bin window and tree coordinates. For the - * former you can use [method@Gtk.TreeView.convert_widget_to_bin_window_coords] - * (and vice versa), for the latter [method@Gtk.TreeView.convert_bin_window_to_tree_coords] - * (and vice versa). - * - * ## `GtkTreeView` as `GtkBuildable` - * - * The `GtkTreeView` implementation of the `GtkBuildable` interface accepts - * [class@Gtk.TreeViewColumn] objects as `` elements and exposes the - * internal [class@Gtk.TreeSelection] in UI definitions. - * - * An example of a UI definition fragment with `GtkTreeView`: - * - * ```xml - * - * liststore1 - * - * - * Test - * - * - * - * 1 - * - * - * - * - * - * - * - * - * - * - * ``` - * - * ## CSS nodes - * - * ``` - * treeview.view - * ├── header - * │ ├── button - * │ │ ╰── [sort-indicator] - * ┊ ┊ - * │ ╰── button - * │ ╰── [sort-indicator] - * │ - * ├── [rubberband] - * ╰── [dndtarget] - * ``` - * - * `GtkTreeView` has a main CSS node with name `treeview` and style class `.view`. - * It has a subnode with name `header`, which is the parent for all the column - * header widgets' CSS nodes. - * - * Each column header consists of a `button`, which among other content, has a - * child with name `sort-indicator`, which carries the `.ascending` or `.descending` - * style classes when the column header should show a sort indicator. The CSS - * is expected to provide a suitable image using the `-gtk-icon-source` property. - * - * For rubberband selection, a subnode with name `rubberband` is used. - * - * For the drop target location during DND, a subnode with name `dndtarget` is used. - */ - -enum -{ - DRAG_COLUMN_WINDOW_STATE_UNSET = 0, - DRAG_COLUMN_WINDOW_STATE_ORIGINAL = 1, - DRAG_COLUMN_WINDOW_STATE_ARROW = 2, - DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT = 3, - DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT = 4 -}; - -enum -{ - RUBBER_BAND_OFF = 0, - RUBBER_BAND_MAYBE_START = 1, - RUBBER_BAND_ACTIVE = 2 -}; - -typedef enum { - CLEAR_AND_SELECT = (1 << 0), - CLAMP_NODE = (1 << 1), - CURSOR_INVALID = (1 << 2) -} SetCursorFlags; - - /* This lovely little value is used to determine how far away from the title bar - * you can move the mouse and still have a column drag work. - */ -#define TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER(tree_view) (10*gtk_tree_view_get_effective_header_height(tree_view)) - -#ifdef __GNUC__ - -#define TREE_VIEW_INTERNAL_ASSERT(expr, ret) G_STMT_START{ \ - if (!(expr)) \ - { \ - g_log (G_LOG_DOMAIN, \ - G_LOG_LEVEL_CRITICAL, \ - "%s (%s): assertion `%s' failed.\n" \ - "There is a disparity between the internal view of the GtkTreeView,\n" \ - "and the GtkTreeModel. This generally means that the model has changed\n"\ - "without letting the view know. Any display from now on is likely to\n" \ - "be incorrect.\n", \ - G_STRLOC, \ - G_STRFUNC, \ - #expr); \ - return ret; \ - }; }G_STMT_END - -#define TREE_VIEW_INTERNAL_ASSERT_VOID(expr) G_STMT_START{ \ - if (!(expr)) \ - { \ - g_log (G_LOG_DOMAIN, \ - G_LOG_LEVEL_CRITICAL, \ - "%s (%s): assertion `%s' failed.\n" \ - "There is a disparity between the internal view of the GtkTreeView,\n" \ - "and the GtkTreeModel. This generally means that the model has changed\n"\ - "without letting the view know. Any display from now on is likely to\n" \ - "be incorrect.\n", \ - G_STRLOC, \ - G_STRFUNC, \ - #expr); \ - return; \ - }; }G_STMT_END - -#else - -#define TREE_VIEW_INTERNAL_ASSERT(expr, ret) G_STMT_START{ \ - if (!(expr)) \ - { \ - g_log (G_LOG_DOMAIN, \ - G_LOG_LEVEL_CRITICAL, \ - "file %s: line %d: assertion `%s' failed.\n" \ - "There is a disparity between the internal view of the GtkTreeView,\n" \ - "and the GtkTreeModel. This generally means that the model has changed\n"\ - "without letting the view know. Any display from now on is likely to\n" \ - "be incorrect.\n", \ - __FILE__, \ - __LINE__, \ - #expr); \ - return ret; \ - }; }G_STMT_END - -#define TREE_VIEW_INTERNAL_ASSERT_VOID(expr) G_STMT_START{ \ - if (!(expr)) \ - { \ - g_log (G_LOG_DOMAIN, \ - G_LOG_LEVEL_CRITICAL, \ - "file %s: line %d: assertion '%s' failed.\n" \ - "There is a disparity between the internal view of the GtkTreeView,\n" \ - "and the GtkTreeModel. This generally means that the model has changed\n"\ - "without letting the view know. Any display from now on is likely to\n" \ - "be incorrect.\n", \ - __FILE__, \ - __LINE__, \ - #expr); \ - return; \ - }; }G_STMT_END -#endif - -#define GTK_TREE_VIEW_PRIORITY_VALIDATE (GDK_PRIORITY_REDRAW + 5) -#define GTK_TREE_VIEW_PRIORITY_SCROLL_SYNC (GTK_TREE_VIEW_PRIORITY_VALIDATE + 2) -/* 3/5 of gdkframeclockidle.c's FRAME_INTERVAL (16667 microsecs) */ -#define GTK_TREE_VIEW_TIME_MS_PER_IDLE 10 -#define SCROLL_EDGE_SIZE 15 -#define GTK_TREE_VIEW_SEARCH_DIALOG_TIMEOUT 5000 -#define AUTO_EXPAND_TIMEOUT 500 - -/* Translate from bin_window coordinates to rbtree (tree coordinates) and - * vice versa. - */ -#define TREE_WINDOW_Y_TO_RBTREE_Y(tree_view,y) ((y) + tree_view->dy) -#define RBTREE_Y_TO_TREE_WINDOW_Y(tree_view,y) ((y) - tree_view->dy) - -/* Vertical separator width. Must be an even number. */ -#define _TREE_VIEW_VERTICAL_SEPARATOR 2 -/* Horizontal separator width. Must be an even number. */ -#define _TREE_VIEW_HORIZONTAL_SEPARATOR 4 -/* Tree view grid line width, in pixels */ -#define _TREE_VIEW_GRID_LINE_WIDTH 1 -/* Tree view line width, in pixels */ -#define _TREE_VIEW_TREE_LINE_WIDTH 1 - -typedef struct _GtkTreeViewColumnReorder GtkTreeViewColumnReorder; -struct _GtkTreeViewColumnReorder -{ - int left_align; - int right_align; - GtkTreeViewColumn *left_column; - GtkTreeViewColumn *right_column; -}; - -typedef struct _GtkTreeViewChild GtkTreeViewChild; -struct _GtkTreeViewChild -{ - GtkWidget *widget; - GtkTreeRBNode *node; - GtkTreeRBTree *tree; - GtkTreeViewColumn *column; - GtkBorder border; -}; - - -typedef struct _TreeViewDragInfo TreeViewDragInfo; -struct _TreeViewDragInfo -{ - GdkContentFormats *source_formats; - GdkDragAction source_actions; - GdkDrag *drag; - GtkTreeRowReference *source_item; - - GtkCssNode *cssnode; - GtkDropTargetAsync *dest; - GdkModifierType start_button_mask; - - guint source_set : 1; - guint dest_set : 1; -}; - - -typedef struct -{ - GtkTreeModel *model; - - /* tree information */ - GtkTreeRBTree *tree; - - /* Container info */ - GList *children; - int width; - - guint presize_handler_tick_cb; - - /* Adjustments */ - GtkAdjustment *hadjustment; - GtkAdjustment *vadjustment; - int min_display_width; - int min_display_height; - - /* CSS nodes */ - GtkCssNode *header_node; - - /* Scroll position state keeping */ - GtkTreeRowReference *top_row; - int top_row_dy; - /* dy == y pos of top_row + top_row_dy */ - /* we cache it for simplicity of the code */ - int dy; - - guint validate_rows_timer; - guint scroll_sync_timer; - - /* Indentation and expander layout */ - GtkTreeViewColumn *expander_column; - - int level_indentation; - - /* Key navigation (focus), selection */ - int cursor_offset; - - GtkTreeRowReference *anchor; - GtkTreeRBNode *cursor_node; - GtkTreeRBTree *cursor_tree; - - GtkTreeViewColumn *focus_column; - - /* Current pressed node, previously pressed, prelight */ - GtkTreeRBNode *button_pressed_node; - GtkTreeRBTree *button_pressed_tree; - - int press_start_x; - int press_start_y; - - int event_last_x; - int event_last_y; - - GtkTreeRBNode *prelight_node; - GtkTreeRBTree *prelight_tree; - - /* Cell Editing */ - GtkTreeViewColumn *edited_column; - - /* Auto expand/collapse timeout in hover mode */ - guint auto_expand_timeout; - - /* Selection information */ - GtkTreeSelection *selection; - - /* Header information */ - int header_height; - int n_columns; - GList *columns; - - GtkTreeViewColumnDropFunc column_drop_func; - gpointer column_drop_func_data; - GDestroyNotify column_drop_func_data_destroy; - GList *column_drag_info; - GtkTreeViewColumnReorder *cur_reorder; - - int prev_width_before_expander; - - /* Scroll timeout (e.g. during dnd, rubber banding) */ - guint scroll_timeout; - - /* Interactive Header reordering */ - GtkTreeViewColumn *drag_column; - int drag_column_x; - int drag_column_y; - - /* Interactive Header Resizing */ - int drag_pos; - int x_drag; - - /* Row drag-and-drop */ - GtkTreeRowReference *drag_dest_row; - GtkTreeViewDropPosition drag_dest_pos; - guint open_dest_timeout; - - /* Rubber banding */ - int rubber_band_status; - int rubber_band_x; - int rubber_band_y; - int rubber_band_extend; - int rubber_band_modify; - - /* fixed height */ - int fixed_height; - - GtkTreeRBNode *rubber_band_start_node; - GtkTreeRBTree *rubber_band_start_tree; - - GtkTreeRBNode *rubber_band_end_node; - GtkTreeRBTree *rubber_band_end_tree; - GtkCssNode *rubber_band_cssnode; - - /* Scroll-to functionality when unrealized */ - GtkTreeRowReference *scroll_to_path; - GtkTreeViewColumn *scroll_to_column; - float scroll_to_row_align; - float scroll_to_col_align; - - /* Interactive search */ - int selected_iter; - int search_column; - GtkTreeViewSearchEqualFunc search_equal_func; - gpointer search_user_data; - GDestroyNotify search_destroy; - gpointer search_position_user_data; - GDestroyNotify search_position_destroy; - GtkWidget *search_popover; - GtkWidget *search_entry; - gulong search_entry_changed_id; - guint typeselect_flush_timeout; - - /* Grid and tree lines */ - GtkTreeViewGridLines grid_lines; - gboolean tree_lines_enabled; - - /* Row separators */ - GtkTreeViewRowSeparatorFunc row_separator_func; - gpointer row_separator_data; - GDestroyNotify row_separator_destroy; - - /* Gestures */ - GtkGesture *click_gesture; - GtkGesture *drag_gesture; /* Rubberbanding, row DnD */ - GtkGesture *column_drag_gesture; /* Column reordering, resizing */ - - /* Tooltip support */ - int tooltip_column; - - int expander_size; - - GdkRGBA grid_line_color; /* Color used in the textures */ - GdkTexture *horizontal_grid_line_texture; - GdkTexture *vertical_grid_line_texture; - - GdkRGBA tree_line_color; /* Color used in the textures */ - GdkTexture *horizontal_tree_line_texture; - GdkTexture *vertical_tree_line_texture; - - /* Here comes the bitfield */ - guint scroll_to_use_align : 1; - - guint fixed_height_mode : 1; - guint fixed_height_check : 1; - - guint activate_on_single_click : 1; - guint reorderable : 1; - guint header_has_focus : 1; - guint drag_column_surface_state : 3; - guint mark_rows_col_dirty : 1; - - /* for DnD */ - guint empty_view_drop : 1; - - guint modify_selection_pressed : 1; - guint extend_selection_pressed : 1; - - guint in_top_row_to_dy : 1; - - /* interactive search */ - guint enable_search : 1; - guint disable_popdown : 1; - guint search_custom_entry_set : 1; - - guint hover_selection : 1; - guint hover_expand : 1; - guint imcontext_changed : 1; - - guint in_scroll : 1; - - guint rubber_banding_enable : 1; - - guint in_grab : 1; - - /* Whether our key press handler is to avoid sending an unhandled binding to the search entry */ - guint search_entry_avoid_unhandled_binding : 1; - - /* GtkScrollablePolicy needs to be checked when - * driving the scrollable adjustment values */ - guint hscroll_policy : 1; - guint vscroll_policy : 1; - - /* GtkTreeView flags */ - guint is_list : 1; - guint show_expanders : 1; - guint in_column_resize : 1; - guint arrow_prelit : 1; - guint headers_visible : 1; - guint draw_keyfocus : 1; - guint model_setup : 1; - guint in_column_drag : 1; -} GtkTreeViewPrivate; - - -/* Signals */ -enum -{ - ROW_ACTIVATED, - TEST_EXPAND_ROW, - TEST_COLLAPSE_ROW, - ROW_EXPANDED, - ROW_COLLAPSED, - COLUMNS_CHANGED, - CURSOR_CHANGED, - MOVE_CURSOR, - SELECT_ALL, - UNSELECT_ALL, - SELECT_CURSOR_ROW, - TOGGLE_CURSOR_ROW, - EXPAND_COLLAPSE_CURSOR_ROW, - SELECT_CURSOR_PARENT, - START_INTERACTIVE_SEARCH, - LAST_SIGNAL -}; - -/* Properties */ -enum { - PROP_0, - PROP_MODEL, - PROP_HEADERS_VISIBLE, - PROP_HEADERS_CLICKABLE, - PROP_EXPANDER_COLUMN, - PROP_REORDERABLE, - PROP_ENABLE_SEARCH, - PROP_SEARCH_COLUMN, - PROP_FIXED_HEIGHT_MODE, - PROP_HOVER_SELECTION, - PROP_HOVER_EXPAND, - PROP_SHOW_EXPANDERS, - PROP_LEVEL_INDENTATION, - PROP_RUBBER_BANDING, - PROP_ENABLE_GRID_LINES, - PROP_ENABLE_TREE_LINES, - PROP_TOOLTIP_COLUMN, - PROP_ACTIVATE_ON_SINGLE_CLICK, - LAST_PROP, - /* overridden */ - PROP_HADJUSTMENT = LAST_PROP, - PROP_VADJUSTMENT, - PROP_HSCROLL_POLICY, - PROP_VSCROLL_POLICY, -}; - -/* object signals */ -static void gtk_tree_view_finalize (GObject *object); -static void gtk_tree_view_dispose (GObject *object); -static void gtk_tree_view_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec); -static void gtk_tree_view_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec); - -/* gtkwidget signals */ -static void gtk_tree_view_realize (GtkWidget *widget); -static void gtk_tree_view_unrealize (GtkWidget *widget); -static void gtk_tree_view_unroot (GtkWidget *widget); -static void gtk_tree_view_map (GtkWidget *widget); -static void gtk_tree_view_measure (GtkWidget *widget, - GtkOrientation orientation, - int for_size, - int *minimum, - int *natural, - int *minimum_baseline, - int *natural_baseline); -static void gtk_tree_view_size_allocate (GtkWidget *widget, - int width, - int height, - int baseline); -static void gtk_tree_view_snapshot (GtkWidget *widget, - GtkSnapshot *snapshot); - -static gboolean gtk_tree_view_forward_controller_key_pressed (GtkEventControllerKey *key, - guint keyval, - guint keycode, - GdkModifierType state, - GtkTreeView *tree_view); -static gboolean gtk_tree_view_key_controller_key_pressed (GtkEventControllerKey *key, - guint keyval, - guint keycode, - GdkModifierType state, - GtkTreeView *tree_view); -static void gtk_tree_view_key_controller_key_released (GtkEventControllerKey *key, - guint keyval, - guint keycode, - GdkModifierType state, - GtkTreeView *tree_view); -static void gtk_tree_view_focus_controller_focus_out (GtkEventController *focus, - GtkTreeView *tree_view); - -static int gtk_tree_view_focus (GtkWidget *widget, - GtkDirectionType direction); -static gboolean gtk_tree_view_grab_focus (GtkWidget *widget); -static void gtk_tree_view_css_changed (GtkWidget *widget, - GtkCssStyleChange *change); - -static void gtk_tree_view_remove (GtkTreeView *tree_view, - GtkWidget *widget); - -/* Source side drag signals */ -static void gtk_tree_view_dnd_finished_cb (GdkDrag *drag, - GtkWidget *widget); -static GdkContentProvider * gtk_tree_view_drag_data_get (GtkTreeView *tree_view, - GtkTreePath *source_row); - -/* Target side drag signals */ -static void gtk_tree_view_drag_leave (GtkDropTargetAsync *dest, - GdkDrop *drop, - GtkTreeView *tree_view); -static GdkDragAction gtk_tree_view_drag_motion (GtkDropTargetAsync *dest, - GdkDrop *drop, - double x, - double y, - GtkTreeView *tree_view); -static gboolean gtk_tree_view_drag_drop (GtkDropTargetAsync *dest, - GdkDrop *drop, - double x, - double y, - GtkTreeView *tree_view); -static void gtk_tree_view_drag_data_received (GObject *source, - GAsyncResult *result, - gpointer data); - -/* tree_model signals */ -static gboolean gtk_tree_view_real_move_cursor (GtkTreeView *tree_view, - GtkMovementStep step, - int count, - gboolean extend, - gboolean modify); -static gboolean gtk_tree_view_real_select_all (GtkTreeView *tree_view); -static gboolean gtk_tree_view_real_unselect_all (GtkTreeView *tree_view); -static gboolean gtk_tree_view_real_select_cursor_row (GtkTreeView *tree_view, - gboolean start_editing); -static gboolean gtk_tree_view_real_toggle_cursor_row (GtkTreeView *tree_view); -static gboolean gtk_tree_view_real_expand_collapse_cursor_row (GtkTreeView *tree_view, - gboolean logical, - gboolean expand, - gboolean open_all); -static gboolean gtk_tree_view_real_select_cursor_parent (GtkTreeView *tree_view); -static void gtk_tree_view_row_changed (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data); -static void gtk_tree_view_row_inserted (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data); -static void gtk_tree_view_row_has_child_toggled (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data); -static void gtk_tree_view_row_deleted (GtkTreeModel *model, - GtkTreePath *path, - gpointer data); -static void gtk_tree_view_rows_reordered (GtkTreeModel *model, - GtkTreePath *parent, - GtkTreeIter *iter, - int *new_order, - gpointer data); - -/* Incremental reflow */ -static gboolean validate_row (GtkTreeView *tree_view, - GtkTreeRBTree *tree, - GtkTreeRBNode *node, - GtkTreeIter *iter, - GtkTreePath *path); -static void validate_visible_area (GtkTreeView *tree_view); -static gboolean do_validate_rows (GtkTreeView *tree_view, - gboolean queue_resize); -static gboolean validate_rows (GtkTreeView *tree_view); -static void install_presize_handler (GtkTreeView *tree_view); -static void install_scroll_sync_handler (GtkTreeView *tree_view); -static void gtk_tree_view_set_top_row (GtkTreeView *tree_view, - GtkTreePath *path, - int offset); -static void gtk_tree_view_dy_to_top_row (GtkTreeView *tree_view); -static void gtk_tree_view_top_row_to_dy (GtkTreeView *tree_view); -static void invalidate_empty_focus (GtkTreeView *tree_view); - -/* Internal functions */ -static gboolean gtk_tree_view_is_expander_column (GtkTreeView *tree_view, - GtkTreeViewColumn *column); -static inline gboolean gtk_tree_view_draw_expanders (GtkTreeView *tree_view); -static void gtk_tree_view_add_move_binding (GtkWidgetClass *widget_class, - guint keyval, - guint modmask, - gboolean add_shifted_binding, - GtkMovementStep step, - int count); -static int gtk_tree_view_unref_and_check_selection_tree (GtkTreeView *tree_view, - GtkTreeRBTree *tree); -static void gtk_tree_view_snapshot_arrow (GtkTreeView *tree_view, - GtkSnapshot *snapshot, - GtkTreeRBTree *tree, - GtkTreeRBNode *node); -static void gtk_tree_view_get_arrow_xrange (GtkTreeView *tree_view, - GtkTreeRBTree *tree, - int *x1, - int *x2); -static void gtk_tree_view_adjustment_changed (GtkAdjustment *adjustment, - GtkTreeView *tree_view); -static void gtk_tree_view_build_tree (GtkTreeView *tree_view, - GtkTreeRBTree *tree, - GtkTreeIter *iter, - int depth, - gboolean recurse); -static void gtk_tree_view_clamp_node_visible (GtkTreeView *tree_view, - GtkTreeRBTree *tree, - GtkTreeRBNode *node); -static void gtk_tree_view_clamp_column_visible (GtkTreeView *tree_view, - GtkTreeViewColumn *column, - gboolean focus_to_cell); -static gboolean gtk_tree_view_maybe_begin_dragging_row (GtkTreeView *tree_view); -static void gtk_tree_view_focus_to_cursor (GtkTreeView *tree_view); -static void gtk_tree_view_move_cursor_up_down (GtkTreeView *tree_view, - int count); -static void gtk_tree_view_move_cursor_page_up_down (GtkTreeView *tree_view, - int count); -static void gtk_tree_view_move_cursor_left_right (GtkTreeView *tree_view, - int count); -static void gtk_tree_view_move_cursor_start_end (GtkTreeView *tree_view, - int count); -static gboolean gtk_tree_view_real_collapse_row (GtkTreeView *tree_view, - GtkTreePath *path, - GtkTreeRBTree *tree, - GtkTreeRBNode *node); -static gboolean gtk_tree_view_real_expand_row (GtkTreeView *tree_view, - GtkTreePath *path, - GtkTreeRBTree *tree, - GtkTreeRBNode *node, - gboolean open_all); -static void gtk_tree_view_real_set_cursor (GtkTreeView *tree_view, - GtkTreePath *path, - SetCursorFlags flags); -static gboolean gtk_tree_view_has_can_focus_cell (GtkTreeView *tree_view); -static void column_sizing_notify (GObject *object, - GParamSpec *pspec, - gpointer data); -static void gtk_tree_view_stop_rubber_band (GtkTreeView *tree_view); -static void ensure_unprelighted (GtkTreeView *tree_view); -static void update_prelight (GtkTreeView *tree_view, - int x, - int y); - -static inline int gtk_tree_view_get_effective_header_height (GtkTreeView *tree_view); - -static inline int gtk_tree_view_get_cell_area_y_offset (GtkTreeView *tree_view, - GtkTreeRBTree *tree, - GtkTreeRBNode *node); -static inline int gtk_tree_view_get_cell_area_height (GtkTreeView *tree_view, - GtkTreeRBNode *node); - -static inline int gtk_tree_view_get_row_y_offset (GtkTreeView *tree_view, - GtkTreeRBTree *tree, - GtkTreeRBNode *node); -static inline int gtk_tree_view_get_row_height (GtkTreeView *tree_view, - GtkTreeRBNode *node); -static TreeViewDragInfo* get_info (GtkTreeView *tree_view); - -/* interactive search */ -static void gtk_tree_view_ensure_interactive_directory (GtkTreeView *tree_view); -static void gtk_tree_view_search_popover_hide (GtkWidget *search_popover, - GtkTreeView *tree_view); -static void gtk_tree_view_search_preedit_changed (GtkText *text, - const char *preedit, - GtkTreeView *tree_view); -static void gtk_tree_view_search_changed (GtkEditable *editable, - GtkTreeView *tree_view); -static void gtk_tree_view_search_activate (GtkEntry *entry, - GtkTreeView *tree_view); -static void gtk_tree_view_search_pressed_cb (GtkGesture *gesture, - int n_press, - double x, - double y, - GtkTreeView *tree_view); -static gboolean gtk_tree_view_search_scroll_event (GtkWidget *entry, - double dx, - double dy, - GtkTreeView *tree_view); -static gboolean gtk_tree_view_search_key_pressed (GtkEventControllerKey *key, - guint keyval, - guint keycode, - GdkModifierType state, - GtkTreeView *tree_view); -static gboolean gtk_tree_view_search_move (GtkWidget *window, - GtkTreeView *tree_view, - gboolean up); -static gboolean gtk_tree_view_search_equal_func (GtkTreeModel *model, - int column, - const char *key, - GtkTreeIter *iter, - gpointer search_data); -static gboolean gtk_tree_view_search_iter (GtkTreeModel *model, - GtkTreeSelection *selection, - GtkTreeIter *iter, - const char *text, - int *count, - int n); -static void gtk_tree_view_search_init (GtkWidget *entry, - GtkTreeView *tree_view); -static void gtk_tree_view_put (GtkTreeView *tree_view, - GtkWidget *child_widget, - GtkTreePath *path, - GtkTreeViewColumn*column, - const GtkBorder *border); -static gboolean gtk_tree_view_start_editing (GtkTreeView *tree_view, - GtkTreePath *cursor_path, - gboolean edit_only); -static void gtk_tree_view_stop_editing (GtkTreeView *tree_view, - gboolean cancel_editing); -static gboolean gtk_tree_view_real_start_interactive_search (GtkTreeView *tree_view, - gboolean keybinding); -static gboolean gtk_tree_view_start_interactive_search (GtkTreeView *tree_view); -static GtkTreeViewColumn *gtk_tree_view_get_drop_column (GtkTreeView *tree_view, - GtkTreeViewColumn *column, - int drop_position); - -/* GtkBuildable */ -static void gtk_tree_view_buildable_add_child (GtkBuildable *tree_view, - GtkBuilder *builder, - GObject *child, - const char *type); -static GObject *gtk_tree_view_buildable_get_internal_child (GtkBuildable *buildable, - GtkBuilder *builder, - const char *childname); -static void gtk_tree_view_buildable_init (GtkBuildableIface *iface); - -/* GtkScrollable */ -static void gtk_tree_view_scrollable_init (GtkScrollableInterface *iface); - -static void gtk_tree_view_do_set_hadjustment (GtkTreeView *tree_view, - GtkAdjustment *adjustment); -static void gtk_tree_view_do_set_vadjustment (GtkTreeView *tree_view, - GtkAdjustment *adjustment); - -static gboolean scroll_row_timeout (gpointer data); -static void add_scroll_timeout (GtkTreeView *tree_view); -static void remove_scroll_timeout (GtkTreeView *tree_view); - -static void grab_focus_and_unset_draw_keyfocus (GtkTreeView *tree_view); - -/* Gestures */ -static void gtk_tree_view_column_click_gesture_pressed (GtkGestureClick *gesture, - int n_press, - double x, - double y, - GtkTreeView *tree_view); - -static void gtk_tree_view_click_gesture_pressed (GtkGestureClick *gesture, - int n_press, - double x, - double y, - GtkTreeView *tree_view); -static void gtk_tree_view_click_gesture_released (GtkGestureClick *gesture, - int n_press, - double x, - double y, - GtkTreeView *tree_view); - -static void gtk_tree_view_column_drag_gesture_begin (GtkGestureDrag *gesture, - double start_x, - double start_y, - GtkTreeView *tree_view); -static void gtk_tree_view_column_drag_gesture_update (GtkGestureDrag *gesture, - double offset_x, - double offset_y, - GtkTreeView *tree_view); -static void gtk_tree_view_column_drag_gesture_end (GtkGestureDrag *gesture, - double offset_x, - double offset_y, - GtkTreeView *tree_view); - -static void gtk_tree_view_drag_gesture_begin (GtkGestureDrag *gesture, - double start_x, - double start_y, - GtkTreeView *tree_view); -static void gtk_tree_view_drag_gesture_update (GtkGestureDrag *gesture, - double offset_x, - double offset_y, - GtkTreeView *tree_view); -static void gtk_tree_view_drag_gesture_end (GtkGestureDrag *gesture, - double offset_x, - double offset_y, - GtkTreeView *tree_view); -static void gtk_tree_view_motion_controller_enter (GtkEventControllerMotion *controller, - double x, - double y, - GtkTreeView *tree_view); -static void gtk_tree_view_motion_controller_leave (GtkEventControllerMotion *controller, - GtkTreeView *tree_view); -static void gtk_tree_view_motion_controller_motion (GtkEventControllerMotion *controller, - double x, - double y, - GtkTreeView *tree_view); - -static guint tree_view_signals [LAST_SIGNAL] = { 0 }; -static GParamSpec *tree_view_props [LAST_PROP] = { NULL }; - - - -/* GType Methods - */ - -G_DEFINE_TYPE_WITH_CODE (GtkTreeView, gtk_tree_view, GTK_TYPE_WIDGET, - G_ADD_PRIVATE (GtkTreeView) - G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, - gtk_tree_view_buildable_init) - G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, - gtk_tree_view_scrollable_init)) - -static void -gtk_tree_view_class_init (GtkTreeViewClass *class) -{ - GObjectClass *o_class = G_OBJECT_CLASS (class); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); - - /* GObject signals */ - o_class->set_property = gtk_tree_view_set_property; - o_class->get_property = gtk_tree_view_get_property; - o_class->finalize = gtk_tree_view_finalize; - o_class->dispose = gtk_tree_view_dispose; - - /* GtkWidget signals */ - widget_class->map = gtk_tree_view_map; - widget_class->realize = gtk_tree_view_realize; - widget_class->unrealize = gtk_tree_view_unrealize; - widget_class->unroot = gtk_tree_view_unroot; - widget_class->measure = gtk_tree_view_measure; - widget_class->size_allocate = gtk_tree_view_size_allocate; - widget_class->snapshot = gtk_tree_view_snapshot; - widget_class->focus = gtk_tree_view_focus; - widget_class->grab_focus = gtk_tree_view_grab_focus; - widget_class->css_changed = gtk_tree_view_css_changed; - - class->move_cursor = gtk_tree_view_real_move_cursor; - class->select_all = gtk_tree_view_real_select_all; - class->unselect_all = gtk_tree_view_real_unselect_all; - class->select_cursor_row = gtk_tree_view_real_select_cursor_row; - class->toggle_cursor_row = gtk_tree_view_real_toggle_cursor_row; - class->expand_collapse_cursor_row = gtk_tree_view_real_expand_collapse_cursor_row; - class->select_cursor_parent = gtk_tree_view_real_select_cursor_parent; - class->start_interactive_search = gtk_tree_view_start_interactive_search; - - /* Properties */ - - g_object_class_override_property (o_class, PROP_HADJUSTMENT, "hadjustment"); - g_object_class_override_property (o_class, PROP_VADJUSTMENT, "vadjustment"); - g_object_class_override_property (o_class, PROP_HSCROLL_POLICY, "hscroll-policy"); - g_object_class_override_property (o_class, PROP_VSCROLL_POLICY, "vscroll-policy"); - - tree_view_props[PROP_MODEL] = - g_param_spec_object ("model", NULL, NULL, - GTK_TYPE_TREE_MODEL, - GTK_PARAM_READWRITE); - - tree_view_props[PROP_HEADERS_VISIBLE] = - g_param_spec_boolean ("headers-visible", NULL, NULL, - TRUE, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); - - tree_view_props[PROP_HEADERS_CLICKABLE] = - g_param_spec_boolean ("headers-clickable", NULL, NULL, - TRUE, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); - - tree_view_props[PROP_EXPANDER_COLUMN] = - g_param_spec_object ("expander-column", NULL, NULL, - GTK_TYPE_TREE_VIEW_COLUMN, - GTK_PARAM_READWRITE); - - tree_view_props[PROP_REORDERABLE] = - g_param_spec_boolean ("reorderable", NULL, NULL, - FALSE, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); - - tree_view_props[PROP_ENABLE_SEARCH] = - g_param_spec_boolean ("enable-search", NULL, NULL, - TRUE, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); - - tree_view_props[PROP_SEARCH_COLUMN] = - g_param_spec_int ("search-column", NULL, NULL, - -1, G_MAXINT, - -1, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); - - /** - * GtkTreeView:fixed-height-mode: - * - * Setting the ::fixed-height-mode property to %TRUE speeds up - * `GtkTreeView` by assuming that all rows have the same height. - * Only enable this option if all rows are the same height. - * Please see gtk_tree_view_set_fixed_height_mode() for more - * information on this option. - */ - tree_view_props[PROP_FIXED_HEIGHT_MODE] = - g_param_spec_boolean ("fixed-height-mode", NULL, NULL, - FALSE, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); - - /** - * GtkTreeView:hover-selection: - * - * Enables or disables the hover selection mode of @tree_view. - * Hover selection makes the selected row follow the pointer. - * Currently, this works only for the selection modes - * %GTK_SELECTION_SINGLE and %GTK_SELECTION_BROWSE. - * - * This mode is primarily intended for treeviews in popups, e.g. - * in `GtkComboBox` or `GtkEntryCompletion`. - */ - tree_view_props[PROP_HOVER_SELECTION] = - g_param_spec_boolean ("hover-selection", NULL, NULL, - FALSE, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); - - /** - * GtkTreeView:hover-expand: - * - * Enables or disables the hover expansion mode of @tree_view. - * Hover expansion makes rows expand or collapse if the pointer moves - * over them. - * - * This mode is primarily intended for treeviews in popups, e.g. - * in `GtkComboBox` or `GtkEntryCompletion`. - */ - tree_view_props[PROP_HOVER_EXPAND] = - g_param_spec_boolean ("hover-expand", NULL, NULL, - FALSE, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); - - /** - * GtkTreeView:show-expanders: - * - * %TRUE if the view has expanders. - */ - tree_view_props[PROP_SHOW_EXPANDERS] = - g_param_spec_boolean ("show-expanders", NULL, NULL, - TRUE, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); - - /** - * GtkTreeView:level-indentation: - * - * Extra indentation for each level. - */ - tree_view_props[PROP_LEVEL_INDENTATION] = - g_param_spec_int ("level-indentation", NULL, NULL, - 0, G_MAXINT, - 0, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); - - tree_view_props[PROP_RUBBER_BANDING] = - g_param_spec_boolean ("rubber-banding", NULL, NULL, - FALSE, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); - - tree_view_props[PROP_ENABLE_GRID_LINES] = - g_param_spec_enum ("enable-grid-lines", NULL, NULL, - GTK_TYPE_TREE_VIEW_GRID_LINES, - GTK_TREE_VIEW_GRID_LINES_NONE, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); - - tree_view_props[PROP_ENABLE_TREE_LINES] = - g_param_spec_boolean ("enable-tree-lines", NULL, NULL, - FALSE, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); - - tree_view_props[PROP_TOOLTIP_COLUMN] = - g_param_spec_int ("tooltip-column", NULL, NULL, - -1, G_MAXINT, - -1, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); - - /** - * GtkTreeView:activate-on-single-click: - * - * The activate-on-single-click property specifies whether the "row-activated" signal - * will be emitted after a single click. - */ - tree_view_props[PROP_ACTIVATE_ON_SINGLE_CLICK] = - g_param_spec_boolean ("activate-on-single-click", NULL, NULL, - FALSE, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); - - g_object_class_install_properties (o_class, LAST_PROP, tree_view_props); - - /* Signals */ - /** - * GtkTreeView::row-activated: - * @tree_view: the object on which the signal is emitted - * @path: the `GtkTreePath` for the activated row - * @column: (nullable): the `GtkTreeViewColumn` in which the activation occurred - * - * The "row-activated" signal is emitted when the method - * [`method@Gtk.TreeView.row_activated`] is called. - * - * This signal is emitted when the user double-clicks a treeview row with the - * [property@Gtk.TreeView:activate-on-single-click] property set to %FALSE, - * or when the user single-clicks a row when that property set to %TRUE. - * - * This signal is also emitted when a non-editable row is selected and one - * of the keys: Space, Shift+Space, - * Return or Enter is pressed. - * - * For selection handling refer to the - * [tree widget conceptual overview](section-tree-widget.html) - * as well as `GtkTreeSelection`. - */ - tree_view_signals[ROW_ACTIVATED] = - g_signal_new (I_("row-activated"), - G_TYPE_FROM_CLASS (o_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (GtkTreeViewClass, row_activated), - NULL, NULL, - _gtk_marshal_VOID__BOXED_OBJECT, - G_TYPE_NONE, 2, - GTK_TYPE_TREE_PATH, - GTK_TYPE_TREE_VIEW_COLUMN); - g_signal_set_va_marshaller (tree_view_signals[ROW_ACTIVATED], - G_TYPE_FROM_CLASS (o_class), - _gtk_marshal_VOID__BOXED_OBJECTv); - - /** - * GtkTreeView::test-expand-row: - * @tree_view: the object on which the signal is emitted - * @iter: the tree iter of the row to expand - * @path: a tree path that points to the row - * - * The given row is about to be expanded (show its children nodes). Use this - * signal if you need to control the expandability of individual rows. - * - * Returns: %FALSE to allow expansion, %TRUE to reject - */ - tree_view_signals[TEST_EXPAND_ROW] = - g_signal_new (I_("test-expand-row"), - G_TYPE_FROM_CLASS (o_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkTreeViewClass, test_expand_row), - _gtk_boolean_handled_accumulator, NULL, - _gtk_marshal_BOOLEAN__BOXED_BOXED, - G_TYPE_BOOLEAN, 2, - GTK_TYPE_TREE_ITER, - GTK_TYPE_TREE_PATH); - g_signal_set_va_marshaller (tree_view_signals[TEST_EXPAND_ROW], - G_TYPE_FROM_CLASS (o_class), - _gtk_marshal_BOOLEAN__BOXED_BOXEDv); - - /** - * GtkTreeView::test-collapse-row: - * @tree_view: the object on which the signal is emitted - * @iter: the tree iter of the row to collapse - * @path: a tree path that points to the row - * - * The given row is about to be collapsed (hide its children nodes). Use this - * signal if you need to control the collapsibility of individual rows. - * - * Returns: %FALSE to allow collapsing, %TRUE to reject - */ - tree_view_signals[TEST_COLLAPSE_ROW] = - g_signal_new (I_("test-collapse-row"), - G_TYPE_FROM_CLASS (o_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkTreeViewClass, test_collapse_row), - _gtk_boolean_handled_accumulator, NULL, - _gtk_marshal_BOOLEAN__BOXED_BOXED, - G_TYPE_BOOLEAN, 2, - GTK_TYPE_TREE_ITER, - GTK_TYPE_TREE_PATH); - g_signal_set_va_marshaller (tree_view_signals[TEST_COLLAPSE_ROW], - G_TYPE_FROM_CLASS (o_class), - _gtk_marshal_BOOLEAN__BOXED_BOXEDv); - - /** - * GtkTreeView::row-expanded: - * @tree_view: the object on which the signal is emitted - * @iter: the tree iter of the expanded row - * @path: a tree path that points to the row - * - * The given row has been expanded (child nodes are shown). - */ - tree_view_signals[ROW_EXPANDED] = - g_signal_new (I_("row-expanded"), - G_TYPE_FROM_CLASS (o_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkTreeViewClass, row_expanded), - NULL, NULL, - _gtk_marshal_VOID__BOXED_BOXED, - G_TYPE_NONE, 2, - GTK_TYPE_TREE_ITER, - GTK_TYPE_TREE_PATH); - g_signal_set_va_marshaller (tree_view_signals[ROW_EXPANDED], - G_TYPE_FROM_CLASS (o_class), - _gtk_marshal_VOID__BOXED_BOXEDv); - - /** - * GtkTreeView::row-collapsed: - * @tree_view: the object on which the signal is emitted - * @iter: the tree iter of the collapsed row - * @path: a tree path that points to the row - * - * The given row has been collapsed (child nodes are hidden). - */ - tree_view_signals[ROW_COLLAPSED] = - g_signal_new (I_("row-collapsed"), - G_TYPE_FROM_CLASS (o_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkTreeViewClass, row_collapsed), - NULL, NULL, - _gtk_marshal_VOID__BOXED_BOXED, - G_TYPE_NONE, 2, - GTK_TYPE_TREE_ITER, - GTK_TYPE_TREE_PATH); - g_signal_set_va_marshaller (tree_view_signals[ROW_COLLAPSED], - G_TYPE_FROM_CLASS (o_class), - _gtk_marshal_VOID__BOXED_BOXEDv); - - /** - * GtkTreeView::columns-changed: - * @tree_view: the object on which the signal is emitted - * - * The number of columns of the treeview has changed. - */ - tree_view_signals[COLUMNS_CHANGED] = - g_signal_new (I_("columns-changed"), - G_TYPE_FROM_CLASS (o_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkTreeViewClass, columns_changed), - NULL, NULL, - NULL, - G_TYPE_NONE, 0); - - /** - * GtkTreeView::cursor-changed: - * @tree_view: the object on which the signal is emitted - * - * The position of the cursor (focused cell) has changed. - */ - tree_view_signals[CURSOR_CHANGED] = - g_signal_new (I_("cursor-changed"), - G_TYPE_FROM_CLASS (o_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkTreeViewClass, cursor_changed), - NULL, NULL, - NULL, - G_TYPE_NONE, 0); - - /** - * GtkTreeView::move-cursor: - * @tree_view: the object on which the signal is emitted. - * @step: the granularity of the move, as a `GtkMovementStep`. - * %GTK_MOVEMENT_LOGICAL_POSITIONS, %GTK_MOVEMENT_VISUAL_POSITIONS, - * %GTK_MOVEMENT_DISPLAY_LINES, %GTK_MOVEMENT_PAGES and - * %GTK_MOVEMENT_BUFFER_ENDS are supported. - * %GTK_MOVEMENT_LOGICAL_POSITIONS and %GTK_MOVEMENT_VISUAL_POSITIONS - * are treated identically. - * @direction: the direction to move: +1 to move forwards; -1 to move - * backwards. The resulting movement is undefined for all other values. - * @extend: whether to extend the selection - * @modify: whether to modify the selection - * - * The `GtkTreeView`::move-cursor signal is a [keybinding - * signal][class@Gtk.SignalAction] which gets emitted when the user - * presses one of the cursor keys. - * - * Applications should not connect to it, but may emit it with - * g_signal_emit_by_name() if they need to control the cursor - * programmatically. In contrast to gtk_tree_view_set_cursor() and - * gtk_tree_view_set_cursor_on_cell() when moving horizontally - * `GtkTreeView`::move-cursor does not reset the current selection. - * - * Returns: %TRUE if @step is supported, %FALSE otherwise. - */ - tree_view_signals[MOVE_CURSOR] = - g_signal_new (I_("move-cursor"), - G_TYPE_FROM_CLASS (o_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (GtkTreeViewClass, move_cursor), - NULL, NULL, - _gtk_marshal_BOOLEAN__ENUM_INT_BOOLEAN_BOOLEAN, - G_TYPE_BOOLEAN, 4, - GTK_TYPE_MOVEMENT_STEP, - G_TYPE_INT, - G_TYPE_BOOLEAN, - G_TYPE_BOOLEAN); - g_signal_set_va_marshaller (tree_view_signals[MOVE_CURSOR], - G_TYPE_FROM_CLASS (o_class), - _gtk_marshal_BOOLEAN__ENUM_INT_BOOLEAN_BOOLEANv); - - tree_view_signals[SELECT_ALL] = - g_signal_new (I_("select-all"), - G_TYPE_FROM_CLASS (o_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (GtkTreeViewClass, select_all), - NULL, NULL, - _gtk_marshal_BOOLEAN__VOID, - G_TYPE_BOOLEAN, 0); - g_signal_set_va_marshaller (tree_view_signals[SELECT_ALL], - G_TYPE_FROM_CLASS (o_class), - _gtk_marshal_BOOLEAN__VOIDv); - - tree_view_signals[UNSELECT_ALL] = - g_signal_new (I_("unselect-all"), - G_TYPE_FROM_CLASS (o_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (GtkTreeViewClass, unselect_all), - NULL, NULL, - _gtk_marshal_BOOLEAN__VOID, - G_TYPE_BOOLEAN, 0); - g_signal_set_va_marshaller (tree_view_signals[UNSELECT_ALL], - G_TYPE_FROM_CLASS (o_class), - _gtk_marshal_BOOLEAN__VOIDv); - - tree_view_signals[SELECT_CURSOR_ROW] = - g_signal_new (I_("select-cursor-row"), - G_TYPE_FROM_CLASS (o_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (GtkTreeViewClass, select_cursor_row), - NULL, NULL, - _gtk_marshal_BOOLEAN__BOOLEAN, - G_TYPE_BOOLEAN, 1, - G_TYPE_BOOLEAN); - g_signal_set_va_marshaller (tree_view_signals[SELECT_CURSOR_ROW], - G_TYPE_FROM_CLASS (o_class), - _gtk_marshal_BOOLEAN__BOOLEANv); - - tree_view_signals[TOGGLE_CURSOR_ROW] = - g_signal_new (I_("toggle-cursor-row"), - G_TYPE_FROM_CLASS (o_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (GtkTreeViewClass, toggle_cursor_row), - NULL, NULL, - _gtk_marshal_BOOLEAN__VOID, - G_TYPE_BOOLEAN, 0); - g_signal_set_va_marshaller (tree_view_signals[TOGGLE_CURSOR_ROW], - G_TYPE_FROM_CLASS (o_class), - _gtk_marshal_BOOLEAN__VOIDv); - - tree_view_signals[EXPAND_COLLAPSE_CURSOR_ROW] = - g_signal_new (I_("expand-collapse-cursor-row"), - G_TYPE_FROM_CLASS (o_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (GtkTreeViewClass, expand_collapse_cursor_row), - NULL, NULL, - _gtk_marshal_BOOLEAN__BOOLEAN_BOOLEAN_BOOLEAN, - G_TYPE_BOOLEAN, 3, - G_TYPE_BOOLEAN, - G_TYPE_BOOLEAN, - G_TYPE_BOOLEAN); - g_signal_set_va_marshaller (tree_view_signals[EXPAND_COLLAPSE_CURSOR_ROW], - G_TYPE_FROM_CLASS (o_class), - _gtk_marshal_BOOLEAN__BOOLEAN_BOOLEAN_BOOLEANv); - - tree_view_signals[SELECT_CURSOR_PARENT] = - g_signal_new (I_("select-cursor-parent"), - G_TYPE_FROM_CLASS (o_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (GtkTreeViewClass, select_cursor_parent), - NULL, NULL, - _gtk_marshal_BOOLEAN__VOID, - G_TYPE_BOOLEAN, 0); - g_signal_set_va_marshaller (tree_view_signals[SELECT_CURSOR_PARENT], - G_TYPE_FROM_CLASS (o_class), - _gtk_marshal_BOOLEAN__VOIDv); - - tree_view_signals[START_INTERACTIVE_SEARCH] = - g_signal_new (I_("start-interactive-search"), - G_TYPE_FROM_CLASS (o_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (GtkTreeViewClass, start_interactive_search), - NULL, NULL, - _gtk_marshal_BOOLEAN__VOID, - G_TYPE_BOOLEAN, 0); - g_signal_set_va_marshaller (tree_view_signals[START_INTERACTIVE_SEARCH], - G_TYPE_FROM_CLASS (o_class), - _gtk_marshal_BOOLEAN__VOIDv); - - /* Key bindings */ - gtk_tree_view_add_move_binding (widget_class, GDK_KEY_Up, 0, TRUE, - GTK_MOVEMENT_DISPLAY_LINES, -1); - gtk_tree_view_add_move_binding (widget_class, GDK_KEY_KP_Up, 0, TRUE, - GTK_MOVEMENT_DISPLAY_LINES, -1); - - gtk_tree_view_add_move_binding (widget_class, GDK_KEY_Down, 0, TRUE, - GTK_MOVEMENT_DISPLAY_LINES, 1); - gtk_tree_view_add_move_binding (widget_class, GDK_KEY_KP_Down, 0, TRUE, - GTK_MOVEMENT_DISPLAY_LINES, 1); - - gtk_tree_view_add_move_binding (widget_class, GDK_KEY_p, GDK_CONTROL_MASK, FALSE, - GTK_MOVEMENT_DISPLAY_LINES, -1); - - gtk_tree_view_add_move_binding (widget_class, GDK_KEY_n, GDK_CONTROL_MASK, FALSE, - GTK_MOVEMENT_DISPLAY_LINES, 1); - - gtk_tree_view_add_move_binding (widget_class, GDK_KEY_Home, 0, TRUE, - GTK_MOVEMENT_BUFFER_ENDS, -1); - gtk_tree_view_add_move_binding (widget_class, GDK_KEY_KP_Home, 0, TRUE, - GTK_MOVEMENT_BUFFER_ENDS, -1); - - gtk_tree_view_add_move_binding (widget_class, GDK_KEY_End, 0, TRUE, - GTK_MOVEMENT_BUFFER_ENDS, 1); - gtk_tree_view_add_move_binding (widget_class, GDK_KEY_KP_End, 0, TRUE, - GTK_MOVEMENT_BUFFER_ENDS, 1); - - gtk_tree_view_add_move_binding (widget_class, GDK_KEY_Page_Up, 0, TRUE, - GTK_MOVEMENT_PAGES, -1); - gtk_tree_view_add_move_binding (widget_class, GDK_KEY_KP_Page_Up, 0, TRUE, - GTK_MOVEMENT_PAGES, -1); - - gtk_tree_view_add_move_binding (widget_class, GDK_KEY_Page_Down, 0, TRUE, - GTK_MOVEMENT_PAGES, 1); - gtk_tree_view_add_move_binding (widget_class, GDK_KEY_KP_Page_Down, 0, TRUE, - GTK_MOVEMENT_PAGES, 1); - - gtk_tree_view_add_move_binding (widget_class, GDK_KEY_Right, 0, FALSE, - GTK_MOVEMENT_VISUAL_POSITIONS, 1); - gtk_tree_view_add_move_binding (widget_class, GDK_KEY_Left, 0, FALSE, - GTK_MOVEMENT_VISUAL_POSITIONS, -1); - - gtk_tree_view_add_move_binding (widget_class, GDK_KEY_KP_Right, 0, FALSE, - GTK_MOVEMENT_VISUAL_POSITIONS, 1); - gtk_tree_view_add_move_binding (widget_class, GDK_KEY_KP_Left, 0, FALSE, - GTK_MOVEMENT_VISUAL_POSITIONS, -1); - - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_space, GDK_CONTROL_MASK, "toggle-cursor-row", NULL); - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_KP_Space, GDK_CONTROL_MASK, "toggle-cursor-row", NULL); - - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_a, GDK_CONTROL_MASK, "select-all", NULL); - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_slash, GDK_CONTROL_MASK, "select-all", NULL); - - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_A, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "unselect-all", NULL); - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_backslash, GDK_CONTROL_MASK, "unselect-all", NULL); - - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_space, GDK_SHIFT_MASK, "select-cursor-row", "(b)", TRUE); - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_KP_Space, GDK_SHIFT_MASK, "select-cursor-row", "(b)", TRUE); - - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_space, 0, "select-cursor-row", "(b)", TRUE); - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_KP_Space, 0, "select-cursor-row", "(b)", TRUE); - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_Return, 0, "select-cursor-row", "(b)", TRUE); - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_ISO_Enter, 0, "select-cursor-row", "(b)", TRUE); - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_KP_Enter, 0, "select-cursor-row", "(b)", TRUE); - - /* expand and collapse rows */ - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_plus, 0, - "expand-collapse-cursor-row", - "(bbb)", TRUE, TRUE, FALSE); - - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_asterisk, 0, - "expand-collapse-cursor-row", - "(bbb)", TRUE, TRUE, TRUE); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Multiply, 0, - "expand-collapse-cursor-row", - "(bbb)", TRUE, TRUE, TRUE); - - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_slash, 0, - "expand-collapse-cursor-row", - "(bbb)", TRUE, FALSE, FALSE); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Divide, 0, - "expand-collapse-cursor-row", - "(bbb)", TRUE, FALSE, FALSE); - - /* Not doable on US keyboards */ - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_plus, GDK_SHIFT_MASK, - "expand-collapse-cursor-row", - "(bbb)", TRUE, TRUE, TRUE); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Add, 0, - "expand-collapse-cursor-row", - "(bbb)", TRUE, TRUE, FALSE); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Add, GDK_SHIFT_MASK, - "expand-collapse-cursor-row", - "(bbb)", TRUE, TRUE, TRUE); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Add, GDK_SHIFT_MASK, - "expand-collapse-cursor-row", - "(bbb)", TRUE, TRUE, TRUE); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Right, GDK_SHIFT_MASK, - "expand-collapse-cursor-row", - "(bbb)", FALSE, TRUE, TRUE); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Right, GDK_SHIFT_MASK, - "expand-collapse-cursor-row", - "(bbb)", FALSE, TRUE, TRUE); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Right, GDK_CONTROL_MASK | GDK_SHIFT_MASK, - "expand-collapse-cursor-row", - "(bbb)", FALSE, TRUE, TRUE); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Right, GDK_CONTROL_MASK | GDK_SHIFT_MASK, - "expand-collapse-cursor-row", - "(bbb)", FALSE, TRUE, TRUE); - - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_minus, 0, - "expand-collapse-cursor-row", - "(bbb)", TRUE, FALSE, FALSE); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_minus, GDK_SHIFT_MASK, - "expand-collapse-cursor-row", - "(bbb)", TRUE, FALSE, TRUE); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Subtract, 0, - "expand-collapse-cursor-row", - "(bbb)", TRUE, FALSE, FALSE); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Subtract, GDK_SHIFT_MASK, - "expand-collapse-cursor-row", - "(bbb)", TRUE, FALSE, TRUE); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Left, GDK_SHIFT_MASK, - "expand-collapse-cursor-row", - "(bbb)", FALSE, FALSE, TRUE); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Left, GDK_SHIFT_MASK, - "expand-collapse-cursor-row", - "(bbb)", FALSE, FALSE, TRUE); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Left, GDK_CONTROL_MASK | GDK_SHIFT_MASK, - "expand-collapse-cursor-row", - "(bbb)", FALSE, FALSE, TRUE); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Left, GDK_CONTROL_MASK | GDK_SHIFT_MASK, - "expand-collapse-cursor-row", - "(bbb)", FALSE, FALSE, TRUE); - - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_BackSpace, 0, "select-cursor-parent", NULL); - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_BackSpace, GDK_CONTROL_MASK, "select-cursor-parent", NULL); - - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_f, GDK_CONTROL_MASK, "start-interactive-search", NULL); - - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_F, GDK_CONTROL_MASK, "start-interactive-search", NULL); - - gtk_widget_class_set_css_name (widget_class, I_("treeview")); -} - -static void -gtk_tree_view_init (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkCssNode *widget_node; - GtkGesture *gesture; - GtkEventController *controller; - GtkEventController **controllers; - guint n_controllers, i; - - gtk_widget_set_overflow (GTK_WIDGET (tree_view), GTK_OVERFLOW_HIDDEN); - gtk_widget_set_focusable (GTK_WIDGET (tree_view), TRUE); - - priv->show_expanders = TRUE; - priv->draw_keyfocus = TRUE; - priv->headers_visible = TRUE; - priv->activate_on_single_click = FALSE; - - /* We need some padding */ - priv->dy = 0; - priv->cursor_offset = 0; - priv->n_columns = 0; - priv->header_height = 1; - priv->x_drag = 0; - priv->drag_pos = -1; - priv->header_has_focus = FALSE; - priv->press_start_x = -1; - priv->press_start_y = -1; - priv->reorderable = FALSE; - priv->presize_handler_tick_cb = 0; - priv->scroll_sync_timer = 0; - priv->fixed_height = -1; - priv->fixed_height_mode = FALSE; - priv->fixed_height_check = 0; - priv->selection = _gtk_tree_selection_new_with_tree_view (tree_view); - priv->enable_search = TRUE; - priv->search_column = -1; - priv->search_equal_func = gtk_tree_view_search_equal_func; - priv->search_custom_entry_set = FALSE; - priv->typeselect_flush_timeout = 0; - priv->width = 0; - priv->expander_size = -1; - - priv->hover_selection = FALSE; - priv->hover_expand = FALSE; - - priv->level_indentation = 0; - - priv->rubber_banding_enable = FALSE; - - priv->grid_lines = GTK_TREE_VIEW_GRID_LINES_NONE; - priv->tree_lines_enabled = FALSE; - - priv->tooltip_column = -1; - - priv->event_last_x = -10000; - priv->event_last_y = -10000; - - gtk_tree_view_do_set_vadjustment (tree_view, NULL); - gtk_tree_view_do_set_hadjustment (tree_view, NULL); - - gtk_widget_add_css_class (GTK_WIDGET (tree_view), "view"); - - widget_node = gtk_widget_get_css_node (GTK_WIDGET (tree_view)); - priv->header_node = gtk_css_node_new (); - gtk_css_node_set_name (priv->header_node, g_quark_from_static_string ("header")); - gtk_css_node_set_parent (priv->header_node, widget_node); - gtk_css_node_set_state (priv->header_node, gtk_css_node_get_state (widget_node)); - g_object_unref (priv->header_node); - - controller = gtk_event_controller_key_new (); - g_signal_connect (controller, "key-pressed", - G_CALLBACK (gtk_tree_view_forward_controller_key_pressed), tree_view); - gtk_widget_add_controller (GTK_WIDGET (tree_view), controller); - - controllers = gtk_widget_list_controllers (GTK_WIDGET (tree_view), GTK_PHASE_BUBBLE, &n_controllers); - for (i = 0; i < n_controllers; i ++) - { - controller = controllers[i]; - if (GTK_IS_SHORTCUT_CONTROLLER (controller)) - { - g_object_ref (controller); - gtk_widget_remove_controller (GTK_WIDGET (tree_view), controller); - gtk_widget_add_controller (GTK_WIDGET (tree_view), controller); - break; - } - } - g_free (controllers); - - priv->click_gesture = gtk_gesture_click_new (); - gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (priv->click_gesture), 0); - g_signal_connect (priv->click_gesture, "pressed", - G_CALLBACK (gtk_tree_view_click_gesture_pressed), tree_view); - g_signal_connect (priv->click_gesture, "released", - G_CALLBACK (gtk_tree_view_click_gesture_released), tree_view); - gtk_widget_add_controller (GTK_WIDGET (tree_view), GTK_EVENT_CONTROLLER (priv->click_gesture)); - - gesture = gtk_gesture_click_new (); - g_signal_connect (gesture, "pressed", - G_CALLBACK (gtk_tree_view_column_click_gesture_pressed), tree_view); - gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (gesture), - GTK_PHASE_CAPTURE); - gtk_widget_add_controller (GTK_WIDGET (tree_view), GTK_EVENT_CONTROLLER (gesture)); - - priv->drag_gesture = gtk_gesture_drag_new (); - g_signal_connect (priv->drag_gesture, "drag-begin", - G_CALLBACK (gtk_tree_view_drag_gesture_begin), tree_view); - g_signal_connect (priv->drag_gesture, "drag-update", - G_CALLBACK (gtk_tree_view_drag_gesture_update), tree_view); - g_signal_connect (priv->drag_gesture, "drag-end", - G_CALLBACK (gtk_tree_view_drag_gesture_end), tree_view); - gtk_widget_add_controller (GTK_WIDGET (tree_view), GTK_EVENT_CONTROLLER (priv->drag_gesture)); - - priv->column_drag_gesture = gtk_gesture_drag_new (); - g_signal_connect (priv->column_drag_gesture, "drag-begin", - G_CALLBACK (gtk_tree_view_column_drag_gesture_begin), tree_view); - g_signal_connect (priv->column_drag_gesture, "drag-update", - G_CALLBACK (gtk_tree_view_column_drag_gesture_update), tree_view); - g_signal_connect (priv->column_drag_gesture, "drag-end", - G_CALLBACK (gtk_tree_view_column_drag_gesture_end), tree_view); - gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->column_drag_gesture), - GTK_PHASE_CAPTURE); - gtk_widget_add_controller (GTK_WIDGET (tree_view), GTK_EVENT_CONTROLLER (priv->column_drag_gesture)); - - controller = gtk_event_controller_motion_new (); - g_signal_connect (controller, "enter", - G_CALLBACK (gtk_tree_view_motion_controller_enter), tree_view); - g_signal_connect (controller, "leave", - G_CALLBACK (gtk_tree_view_motion_controller_leave), tree_view); - g_signal_connect (controller, "motion", - G_CALLBACK (gtk_tree_view_motion_controller_motion), tree_view); - gtk_widget_add_controller (GTK_WIDGET (tree_view), controller); - - controller = gtk_event_controller_key_new (); - g_signal_connect (controller, "key-pressed", - G_CALLBACK (gtk_tree_view_key_controller_key_pressed), tree_view); - g_signal_connect (controller, "key-released", - G_CALLBACK (gtk_tree_view_key_controller_key_released), tree_view); - gtk_widget_add_controller (GTK_WIDGET (tree_view), controller); - - controller = gtk_event_controller_focus_new (); - g_signal_connect (controller, "leave", - G_CALLBACK (gtk_tree_view_focus_controller_focus_out), tree_view); - gtk_widget_add_controller (GTK_WIDGET (tree_view), controller); -} - - - -/* GObject Methods - */ - -static void -gtk_tree_view_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - GtkTreeView *tree_view = GTK_TREE_VIEW (object); - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - switch (prop_id) - { - case PROP_MODEL: - gtk_tree_view_set_model (tree_view, g_value_get_object (value)); - break; - case PROP_HADJUSTMENT: - gtk_tree_view_do_set_hadjustment (tree_view, g_value_get_object (value)); - break; - case PROP_VADJUSTMENT: - gtk_tree_view_do_set_vadjustment (tree_view, g_value_get_object (value)); - break; - case PROP_HSCROLL_POLICY: - if (priv->hscroll_policy != g_value_get_enum (value)) - { - priv->hscroll_policy = g_value_get_enum (value); - gtk_widget_queue_resize (GTK_WIDGET (tree_view)); - g_object_notify_by_pspec (object, pspec); - } - break; - case PROP_VSCROLL_POLICY: - if (priv->vscroll_policy != g_value_get_enum (value)) - { - priv->vscroll_policy = g_value_get_enum (value); - gtk_widget_queue_resize (GTK_WIDGET (tree_view)); - g_object_notify_by_pspec (object, pspec); - } - break; - case PROP_HEADERS_VISIBLE: - gtk_tree_view_set_headers_visible (tree_view, g_value_get_boolean (value)); - break; - case PROP_HEADERS_CLICKABLE: - gtk_tree_view_set_headers_clickable (tree_view, g_value_get_boolean (value)); - break; - case PROP_EXPANDER_COLUMN: - gtk_tree_view_set_expander_column (tree_view, g_value_get_object (value)); - break; - case PROP_REORDERABLE: - gtk_tree_view_set_reorderable (tree_view, g_value_get_boolean (value)); - break; - case PROP_ENABLE_SEARCH: - gtk_tree_view_set_enable_search (tree_view, g_value_get_boolean (value)); - break; - case PROP_SEARCH_COLUMN: - gtk_tree_view_set_search_column (tree_view, g_value_get_int (value)); - break; - case PROP_FIXED_HEIGHT_MODE: - gtk_tree_view_set_fixed_height_mode (tree_view, g_value_get_boolean (value)); - break; - case PROP_HOVER_SELECTION: - if (priv->hover_selection != g_value_get_boolean (value)) - { - priv->hover_selection = g_value_get_boolean (value); - g_object_notify_by_pspec (object, pspec); - } - break; - case PROP_HOVER_EXPAND: - if (priv->hover_expand != g_value_get_boolean (value)) - { - priv->hover_expand = g_value_get_boolean (value); - g_object_notify_by_pspec (object, pspec); - } - break; - case PROP_SHOW_EXPANDERS: - gtk_tree_view_set_show_expanders (tree_view, g_value_get_boolean (value)); - break; - case PROP_LEVEL_INDENTATION: - if (priv->level_indentation != g_value_get_int (value)) - { - priv->level_indentation = g_value_get_int (value); - g_object_notify_by_pspec (object, pspec); - } - break; - case PROP_RUBBER_BANDING: - if (priv->rubber_banding_enable != g_value_get_boolean (value)) - { - priv->rubber_banding_enable = g_value_get_boolean (value); - g_object_notify_by_pspec (object, pspec); - } - break; - case PROP_ENABLE_GRID_LINES: - gtk_tree_view_set_grid_lines (tree_view, g_value_get_enum (value)); - break; - case PROP_ENABLE_TREE_LINES: - gtk_tree_view_set_enable_tree_lines (tree_view, g_value_get_boolean (value)); - break; - case PROP_TOOLTIP_COLUMN: - gtk_tree_view_set_tooltip_column (tree_view, g_value_get_int (value)); - break; - case PROP_ACTIVATE_ON_SINGLE_CLICK: - gtk_tree_view_set_activate_on_single_click (tree_view, g_value_get_boolean (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gtk_tree_view_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GtkTreeView *tree_view = GTK_TREE_VIEW (object); - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - switch (prop_id) - { - case PROP_MODEL: - g_value_set_object (value, priv->model); - break; - case PROP_HADJUSTMENT: - g_value_set_object (value, priv->hadjustment); - break; - case PROP_VADJUSTMENT: - g_value_set_object (value, priv->vadjustment); - break; - case PROP_HSCROLL_POLICY: - g_value_set_enum (value, priv->hscroll_policy); - break; - case PROP_VSCROLL_POLICY: - g_value_set_enum (value, priv->vscroll_policy); - break; - case PROP_HEADERS_VISIBLE: - g_value_set_boolean (value, gtk_tree_view_get_headers_visible (tree_view)); - break; - case PROP_HEADERS_CLICKABLE: - g_value_set_boolean (value, gtk_tree_view_get_headers_clickable (tree_view)); - break; - case PROP_EXPANDER_COLUMN: - g_value_set_object (value, priv->expander_column); - break; - case PROP_REORDERABLE: - g_value_set_boolean (value, priv->reorderable); - break; - case PROP_ENABLE_SEARCH: - g_value_set_boolean (value, priv->enable_search); - break; - case PROP_SEARCH_COLUMN: - g_value_set_int (value, priv->search_column); - break; - case PROP_FIXED_HEIGHT_MODE: - g_value_set_boolean (value, priv->fixed_height_mode); - break; - case PROP_HOVER_SELECTION: - g_value_set_boolean (value, priv->hover_selection); - break; - case PROP_HOVER_EXPAND: - g_value_set_boolean (value, priv->hover_expand); - break; - case PROP_SHOW_EXPANDERS: - g_value_set_boolean (value, priv->show_expanders); - break; - case PROP_LEVEL_INDENTATION: - g_value_set_int (value, priv->level_indentation); - break; - case PROP_RUBBER_BANDING: - g_value_set_boolean (value, priv->rubber_banding_enable); - break; - case PROP_ENABLE_GRID_LINES: - g_value_set_enum (value, priv->grid_lines); - break; - case PROP_ENABLE_TREE_LINES: - g_value_set_boolean (value, priv->tree_lines_enabled); - break; - case PROP_TOOLTIP_COLUMN: - g_value_set_int (value, priv->tooltip_column); - break; - case PROP_ACTIVATE_ON_SINGLE_CLICK: - g_value_set_boolean (value, priv->activate_on_single_click); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gtk_tree_view_finalize (GObject *object) -{ - G_OBJECT_CLASS (gtk_tree_view_parent_class)->finalize (object); -} - - -static GtkBuildableIface *parent_buildable_iface; - -static void -gtk_tree_view_buildable_init (GtkBuildableIface *iface) -{ - parent_buildable_iface = g_type_interface_peek_parent (iface); - iface->add_child = gtk_tree_view_buildable_add_child; - iface->get_internal_child = gtk_tree_view_buildable_get_internal_child; -} - -static void -gtk_tree_view_buildable_add_child (GtkBuildable *tree_view, - GtkBuilder *builder, - GObject *child, - const char *type) -{ - if (GTK_IS_TREE_VIEW_COLUMN (child)) - gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), GTK_TREE_VIEW_COLUMN (child)); - else - parent_buildable_iface->add_child (tree_view, builder, child, type); -} - -static GObject * -gtk_tree_view_buildable_get_internal_child (GtkBuildable *buildable, - GtkBuilder *builder, - const char *childname) -{ - GtkTreeView *tree_view = GTK_TREE_VIEW (buildable); - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - if (strcmp (childname, "selection") == 0) - return G_OBJECT (priv->selection); - - return parent_buildable_iface->get_internal_child (buildable, - builder, - childname); -} - -/* GtkWidget Methods - */ - -static void -gtk_tree_view_free_rbtree (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - gtk_tree_rbtree_free (priv->tree); - - priv->tree = NULL; - priv->button_pressed_node = NULL; - priv->button_pressed_tree = NULL; - priv->prelight_tree = NULL; - priv->prelight_node = NULL; -} - -static void -gtk_tree_view_destroy_search_popover (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - gtk_widget_unparent (priv->search_popover); - - priv->search_popover = NULL; - priv->search_entry = NULL; - priv->search_entry_changed_id = 0; -} - -static void -gtk_tree_view_dispose (GObject *object) -{ - GtkTreeView *tree_view = GTK_TREE_VIEW (object); - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GList *list; - - gtk_tree_view_stop_editing (tree_view, TRUE); - gtk_tree_view_stop_rubber_band (tree_view); - - if (priv->columns != NULL) - { - list = priv->columns; - while (list) - { - GtkTreeViewColumn *column; - column = GTK_TREE_VIEW_COLUMN (list->data); - list = list->next; - gtk_tree_view_remove_column (tree_view, column); - } - priv->columns = NULL; - } - - if (priv->tree != NULL) - { - gtk_tree_view_unref_and_check_selection_tree (tree_view, priv->tree); - - gtk_tree_view_free_rbtree (tree_view); - } - - if (priv->selection != NULL) - { - _gtk_tree_selection_set_tree_view (priv->selection, NULL); - g_object_unref (priv->selection); - priv->selection = NULL; - } - - g_clear_pointer (&priv->scroll_to_path, gtk_tree_row_reference_free); - g_clear_pointer (&priv->drag_dest_row, gtk_tree_row_reference_free); - g_clear_pointer (&priv->top_row, gtk_tree_row_reference_free); - - if (priv->column_drop_func_data && - priv->column_drop_func_data_destroy) - { - priv->column_drop_func_data_destroy (priv->column_drop_func_data); - priv->column_drop_func_data = NULL; - } - - gtk_tree_row_reference_free (priv->anchor); - priv->anchor = NULL; - - /* destroy interactive search dialog */ - if (priv->search_popover) - { - gtk_tree_view_destroy_search_popover (tree_view); - if (priv->typeselect_flush_timeout) - { - g_source_remove (priv->typeselect_flush_timeout); - priv->typeselect_flush_timeout = 0; - } - } - - if (priv->search_custom_entry_set) - { - GtkEventController *controller; - - g_signal_handlers_disconnect_by_func (priv->search_entry, - G_CALLBACK (gtk_tree_view_search_init), - tree_view); - - if (GTK_IS_ENTRY (priv->search_entry)) - controller = gtk_entry_get_key_controller (GTK_ENTRY (priv->search_entry)); - else - controller = gtk_search_entry_get_key_controller (GTK_SEARCH_ENTRY (priv->search_entry)); - g_signal_handlers_disconnect_by_func (controller, - G_CALLBACK (gtk_tree_view_search_key_pressed), - tree_view); - - g_object_unref (priv->search_entry); - - priv->search_entry = NULL; - priv->search_custom_entry_set = FALSE; - } - - if (priv->search_destroy && priv->search_user_data) - { - priv->search_destroy (priv->search_user_data); - priv->search_user_data = NULL; - } - - if (priv->search_position_destroy && priv->search_position_user_data) - { - priv->search_position_destroy (priv->search_position_user_data); - priv->search_position_user_data = NULL; - } - - if (priv->row_separator_destroy && priv->row_separator_data) - { - priv->row_separator_destroy (priv->row_separator_data); - priv->row_separator_data = NULL; - } - - gtk_tree_view_set_model (tree_view, NULL); - - g_clear_object (&priv->hadjustment); - g_clear_object (&priv->vadjustment); - g_clear_object (&priv->horizontal_grid_line_texture); - g_clear_object (&priv->vertical_grid_line_texture); - g_clear_object (&priv->horizontal_tree_line_texture); - g_clear_object (&priv->vertical_tree_line_texture); - - G_OBJECT_CLASS (gtk_tree_view_parent_class)->dispose (object); -} - -/* GtkWidget::map helper */ -static void -gtk_tree_view_map_buttons (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GList *list; - - g_return_if_fail (gtk_widget_get_mapped (GTK_WIDGET (tree_view))); - - if (priv->headers_visible) - { - GtkTreeViewColumn *column; - GtkWidget *button; - - for (list = priv->columns; list; list = list->next) - { - column = list->data; - button = gtk_tree_view_column_get_button (column); - - if (gtk_tree_view_column_get_visible (column) && button) - gtk_widget_show (button); - - if (gtk_widget_get_visible (button) && - !gtk_widget_get_mapped (button)) - gtk_widget_map (button); - } - } -} - -static void -gtk_tree_view_map (GtkWidget *widget) -{ - GtkTreeView *tree_view = GTK_TREE_VIEW (widget); - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GList *tmp_list; - - GTK_WIDGET_CLASS (gtk_tree_view_parent_class)->map (widget); - - tmp_list = priv->children; - while (tmp_list) - { - GtkTreeViewChild *child = tmp_list->data; - tmp_list = tmp_list->next; - - if (gtk_widget_get_visible (child->widget)) - { - if (!gtk_widget_get_mapped (child->widget)) - gtk_widget_map (child->widget); - } - } - - gtk_tree_view_map_buttons (tree_view); -} - -static void -gtk_tree_view_realize (GtkWidget *widget) -{ - GtkTreeView *tree_view = GTK_TREE_VIEW (widget); - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GList *tmp_list; - - GTK_WIDGET_CLASS (gtk_tree_view_parent_class)->realize (widget); - - for (tmp_list = priv->columns; tmp_list; tmp_list = tmp_list->next) - _gtk_tree_view_column_realize_button (GTK_TREE_VIEW_COLUMN (tmp_list->data)); - - /* Need to call those here, since they create GCs */ - gtk_tree_view_set_grid_lines (tree_view, priv->grid_lines); - gtk_tree_view_set_enable_tree_lines (tree_view, priv->tree_lines_enabled); - - install_presize_handler (tree_view); -} - -static void -gtk_tree_view_unrealize (GtkWidget *widget) -{ - GtkTreeView *tree_view = GTK_TREE_VIEW (widget); - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - if (priv->scroll_timeout != 0) - { - g_source_remove (priv->scroll_timeout); - priv->scroll_timeout = 0; - } - - if (priv->auto_expand_timeout != 0) - { - g_source_remove (priv->auto_expand_timeout); - priv->auto_expand_timeout = 0; - } - - if (priv->open_dest_timeout != 0) - { - g_source_remove (priv->open_dest_timeout); - priv->open_dest_timeout = 0; - } - - if (priv->presize_handler_tick_cb != 0) - { - gtk_widget_remove_tick_callback (widget, priv->presize_handler_tick_cb); - priv->presize_handler_tick_cb = 0; - } - - if (priv->validate_rows_timer != 0) - { - g_source_remove (priv->validate_rows_timer); - priv->validate_rows_timer = 0; - } - - if (priv->scroll_sync_timer != 0) - { - g_source_remove (priv->scroll_sync_timer); - priv->scroll_sync_timer = 0; - } - - if (priv->typeselect_flush_timeout) - { - g_source_remove (priv->typeselect_flush_timeout); - priv->typeselect_flush_timeout = 0; - } - - GTK_WIDGET_CLASS (gtk_tree_view_parent_class)->unrealize (widget); -} - -static void -gtk_tree_view_unroot (GtkWidget *widget) -{ - GtkTreeView *tree_view = GTK_TREE_VIEW (widget); - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - /* break ref cycles */ - g_clear_pointer (&priv->scroll_to_path, gtk_tree_row_reference_free); - g_clear_pointer (&priv->drag_dest_row, gtk_tree_row_reference_free); - g_clear_pointer (&priv->top_row, gtk_tree_row_reference_free); - - GTK_WIDGET_CLASS (gtk_tree_view_parent_class)->unroot (widget); -} - -/* GtkWidget::get_preferred_height helper */ -static void -gtk_tree_view_update_height (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GList *list; - - priv->header_height = 0; - - for (list = priv->columns; list; list = list->next) - { - GtkRequisition requisition; - GtkTreeViewColumn *column = list->data; - GtkWidget *button = gtk_tree_view_column_get_button (column); - - if (button == NULL) - continue; - - gtk_widget_get_preferred_size (button, &requisition, NULL); - priv->header_height = MAX (priv->header_height, requisition.height); - } -} - -static int -gtk_tree_view_get_height (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - if (priv->tree == NULL) - return 0; - else - return priv->tree->root->offset; -} - -static void -gtk_tree_view_measure (GtkWidget *widget, - GtkOrientation orientation, - int for_size, - int *minimum, - int *natural, - int *minimum_baseline, - int *natural_baseline) -{ - GtkTreeView *tree_view = GTK_TREE_VIEW (widget); - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - if (orientation == GTK_ORIENTATION_HORIZONTAL) - { - GList *list; - GtkTreeViewColumn *column; - int width = 0; - - /* we validate some rows initially just to make sure we have some size. - * In practice, with a lot of static lists, this should get a good width. - */ - do_validate_rows (tree_view, FALSE); - - /* keep this in sync with size_allocate below */ - for (list = priv->columns; list; list = list->next) - { - column = list->data; - if (!gtk_tree_view_column_get_visible (column) || column == priv->drag_column) - continue; - - width += _gtk_tree_view_column_request_width (column); - } - - *minimum = *natural = width; - } - else /* VERTICAL */ - { - int height; - - gtk_tree_view_update_height (tree_view); - height = gtk_tree_view_get_height (tree_view) + gtk_tree_view_get_effective_header_height (tree_view); - - *minimum = *natural = height; - } -} - -static int -gtk_tree_view_calculate_width_before_expander (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - int width = 0; - GList *list; - gboolean rtl; - - rtl = (_gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); - for (list = (rtl ? g_list_last (priv->columns) : g_list_first (priv->columns)); - list->data != priv->expander_column; - list = (rtl ? list->prev : list->next)) - { - GtkTreeViewColumn *column = list->data; - - width += gtk_tree_view_column_get_width (column); - } - - return width; -} - -/* GtkWidget::size_allocate helper */ -static void -gtk_tree_view_size_allocate_columns (GtkWidget *widget) -{ - GtkTreeView *tree_view = GTK_TREE_VIEW (widget); - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - const int x_offset = - gtk_adjustment_get_value (priv->hadjustment); - GList *list, *first_column, *last_column; - GtkTreeViewColumn *column; - int widget_width, width = 0; - int extra, extra_per_column; - int full_requested_width = 0; - int number_of_expand_columns = 0; - gboolean rtl; - - for (last_column = g_list_last (priv->columns); - last_column && - !(gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (last_column->data))); - last_column = last_column->prev) - ; - if (last_column == NULL) - return; - - for (first_column = g_list_first (priv->columns); - first_column && - !(gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (first_column->data))); - first_column = first_column->next) - ; - - if (first_column == NULL) - return; - - rtl = (_gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL); - - /* find out how many extra space and expandable columns we have */ - for (list = priv->columns; list != last_column->next; list = list->next) - { - column = (GtkTreeViewColumn *)list->data; - - if (!gtk_tree_view_column_get_visible (column) || column == priv->drag_column) - continue; - - full_requested_width += _gtk_tree_view_column_request_width (column); - - if (gtk_tree_view_column_get_expand (column)) - number_of_expand_columns++; - } - - widget_width = gtk_widget_get_width (widget); - extra = MAX (widget_width - full_requested_width, 0); - - if (number_of_expand_columns > 0) - extra_per_column = extra/number_of_expand_columns; - else - extra_per_column = 0; - - for (list = first_column; - list != last_column->next; - list = list->next) - { - int column_width; - - column = list->data; - column_width = _gtk_tree_view_column_request_width (column); - - if (!gtk_tree_view_column_get_visible (column)) - continue; - - if (column == priv->drag_column) - goto next; - - if (gtk_tree_view_column_get_expand (column)) - { - if (number_of_expand_columns == 1) - { - /* We add the remander to the last column as - * */ - column_width += extra; - } - else - { - column_width += extra_per_column; - extra -= extra_per_column; - number_of_expand_columns --; - } - } - else if (number_of_expand_columns == 0 && - list == last_column) - { - column_width += extra; - } - - if (rtl) - _gtk_tree_view_column_allocate (column, widget_width - width - column_width + x_offset, - column_width, priv->header_height); - else - _gtk_tree_view_column_allocate (column, width + x_offset, - column_width, priv->header_height); - next: - width += column_width; - } - - /* We change the width here. The user might have been resizing columns, - * which changes the total width of the tree view. This is of - * importance for getting the horizontal scroll bar right. - */ - priv->width = width; -} - -/* GtkWidget::size_allocate helper */ -static void -gtk_tree_view_size_allocate_drag_column (GtkWidget *widget) -{ - GtkTreeView *tree_view = GTK_TREE_VIEW (widget); - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkAllocation allocation; - int baseline; - GtkWidget *button; - - if (priv->drag_column == NULL) - return; - - button = gtk_tree_view_column_get_button (priv->drag_column); - - allocation.x = priv->drag_column_x; - allocation.y = priv->drag_column_y; - allocation.width = gtk_widget_get_allocated_width (button); - allocation.height = gtk_widget_get_allocated_height (button); - baseline = gtk_widget_get_allocated_baseline (button); - - gtk_widget_size_allocate (button, &allocation, baseline); -} - -static void -gtk_tree_view_size_allocate (GtkWidget *widget, - int width, - int height, - int baseline) -{ - GtkTreeView *tree_view = GTK_TREE_VIEW (widget); - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GList *tmp_list; - double page_size; - - /* We allocate the columns first because the width of the - * tree view (used in updating the adjustments below) might change. - */ - gtk_tree_view_size_allocate_columns (widget); - gtk_tree_view_size_allocate_drag_column (widget); - - page_size = gtk_adjustment_get_page_size (priv->vadjustment); - gtk_adjustment_configure (priv->hadjustment, - gtk_adjustment_get_value (priv->hadjustment) + - (_gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL ? width - page_size : 0), - 0, - MAX (width, priv->width), - width * 0.1, - width * 0.9, - width); - - page_size = height - gtk_tree_view_get_effective_header_height (tree_view); - gtk_adjustment_configure (priv->vadjustment, - gtk_adjustment_get_value (priv->vadjustment), - 0, - MAX (page_size, gtk_tree_view_get_height (tree_view)), - page_size * 0.1, - page_size * 0.9, - page_size); - - /* now the adjustments and window sizes are in sync, we can sync toprow/dy again */ - if (gtk_tree_row_reference_valid (priv->top_row)) - gtk_tree_view_top_row_to_dy (tree_view); - else - gtk_tree_view_dy_to_top_row (tree_view); - - if (gtk_widget_get_realized (widget)) - { - if (priv->tree == NULL) - invalidate_empty_focus (tree_view); - - if (priv->expander_column) - { - /* Might seem awkward, but is the best heuristic I could come up - * with. Only if the width of the columns before the expander - * changes, we will update the prelight status. It is this - * width that makes the expander move vertically. Always updating - * prelight status causes trouble with hover selections. - */ - int width_before_expander; - - width_before_expander = gtk_tree_view_calculate_width_before_expander (tree_view); - - if (priv->prev_width_before_expander - != width_before_expander) - update_prelight (tree_view, - priv->event_last_x, - priv->event_last_y); - - priv->prev_width_before_expander = width_before_expander; - } - } - - for (tmp_list = priv->children; tmp_list; tmp_list = tmp_list->next) - { - GtkTreeViewChild *child = tmp_list->data; - GtkTreePath *path; - GdkRectangle child_rect; - int min_x, max_x, min_y, max_y; - int size; - GtkTextDirection direction; - - direction = _gtk_widget_get_direction (child->widget); - path = _gtk_tree_path_new_from_rbtree (child->tree, child->node); - gtk_tree_view_get_cell_area (tree_view, path, child->column, &child_rect); - child_rect.x += child->border.left; - child_rect.y += child->border.top; - child_rect.width -= child->border.left + child->border.right; - child_rect.height -= child->border.top + child->border.bottom; - - gtk_widget_measure (GTK_WIDGET (child->widget), GTK_ORIENTATION_HORIZONTAL, -1, - &size, NULL, NULL, NULL); - - if (size > child_rect.width) - { - /* Enlarge the child, extending it to the left (RTL) */ - if (direction == GTK_TEXT_DIR_RTL) - child_rect.x -= (size - child_rect.width); - /* or to the right (LTR) */ - else - child_rect.x += 0; - - child_rect.width = size; - } - - gtk_widget_measure (GTK_WIDGET (child->widget), GTK_ORIENTATION_VERTICAL, - child_rect.width, - &size, NULL, - NULL, NULL); - if (size > child_rect.height) - { - /* Enlarge the child, extending in both directions equally */ - child_rect.y -= (size - child_rect.height) / 2; - child_rect.height = size; - } - - /* push the rect back in the visible area if needed, - * preferring the top left corner (for RTL) - * or top right corner (for LTR) - */ - min_x = 0; - max_x = min_x + width - child_rect.width; - min_y = 0; - max_y = min_y + height - gtk_tree_view_get_effective_header_height (tree_view) - child_rect.height; - - if (direction == GTK_TEXT_DIR_LTR) - /* Ensure that child's right edge is not sticking to the right - * (if (child_rect.x > max_x) child_rect.x = max_x), - * then ensure that child's left edge is visible and is not sticking to the left - * (if (child_rect.x < min_x) child_rect.x = min_x). - */ - child_rect.x = MAX (min_x, MIN (max_x, child_rect.x)); - else - /* Ensure that child's left edge is not sticking to the left - * (if (child_rect.x < min_x) child_rect.x = min_x), - * then ensure that child's right edge is visible and is not sticking to the right - * (if (child_rect.x > max_x) child_rect.x = max_x). - */ - child_rect.x = MIN (max_x, MAX (min_x, child_rect.x)); - - child_rect.y = MAX (min_y, MIN (max_y, child_rect.y)); - - gtk_tree_path_free (path); - gtk_widget_size_allocate (child->widget, &child_rect, -1); - } - - if (priv->search_popover) - gtk_popover_present (GTK_POPOVER (priv->search_popover)); -} - -/* Grabs the focus and unsets the GTK_TREE_VIEW_DRAW_KEYFOCUS flag */ -static void -grab_focus_and_unset_draw_keyfocus (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkWidget *widget = GTK_WIDGET (tree_view); - - if (gtk_widget_get_focusable (widget) && - !gtk_widget_has_focus (widget)) - gtk_widget_grab_focus (widget); - - priv->draw_keyfocus = 0; -} - -static inline gboolean -row_is_separator (GtkTreeView *tree_view, - GtkTreeIter *iter, - GtkTreePath *path) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - gboolean is_separator = FALSE; - - if (priv->row_separator_func) - { - GtkTreeIter tmpiter; - - if (iter) - tmpiter = *iter; - else - { - if (!gtk_tree_model_get_iter (priv->model, &tmpiter, path)) - return FALSE; - } - - is_separator = priv->row_separator_func (priv->model, - &tmpiter, - priv->row_separator_data); - } - - return is_separator; -} - -static int -gtk_tree_view_get_expander_size (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkStyleContext *context; - GtkCssStyle *style; - int min_width; - int min_height; - int expander_size; - - if (priv->expander_size != -1) - return priv->expander_size; - - context = gtk_widget_get_style_context (GTK_WIDGET (tree_view)); - gtk_style_context_save (context); - gtk_style_context_add_class (context, "expander"); - - style = gtk_style_context_lookup_style (context); - min_width = _gtk_css_number_value_get (style->size->min_width, 100); - min_height = _gtk_css_number_value_get (style->size->min_height, 100); - - gtk_style_context_restore (context); - - expander_size = MAX (min_width, min_height); - - priv->expander_size = expander_size + (_TREE_VIEW_HORIZONTAL_SEPARATOR / 2); - - return priv->expander_size; -} - -static void -get_current_selection_modifiers (GtkEventController *controller, - gboolean *modify, - gboolean *extend) -{ - GdkModifierType state; - - state = gtk_event_controller_get_current_event_state (controller); - *modify = (state & GDK_CONTROL_MASK) != 0; - *extend = (state & GDK_SHIFT_MASK) != 0; -} - -static void -gtk_tree_view_click_gesture_pressed (GtkGestureClick *gesture, - int n_press, - double x, - double y, - GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkWidget *widget = GTK_WIDGET (tree_view); - GdkRectangle background_area, cell_area; - GtkTreeViewColumn *column = NULL; - GdkEventSequence *sequence; - GdkModifierType modifiers; - GdkEvent *event; - int new_y, y_offset; - int bin_x, bin_y; - GtkTreePath *path; - GtkTreeRBNode *node; - GtkTreeRBTree *tree; - int depth; - guint button; - GList *list; - gboolean rtl; - GtkWidget *target; - - gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, x, y, - &bin_x, &bin_y); - - /* Are we clicking a column header? */ - if (bin_y < 0) - return; - - /* check if this is a click in a child widget */ - target = gtk_event_controller_get_target (GTK_EVENT_CONTROLLER (gesture)); - if (gtk_widget_is_ancestor (target, widget)) - return; - - gtk_tree_view_stop_editing (tree_view, FALSE); - button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture)); - sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture)); - - if (button > 3) - { - gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED); - return; - } - - if (n_press > 1) - gtk_gesture_set_state (priv->drag_gesture, - GTK_EVENT_SEQUENCE_DENIED); - - /* Empty tree? */ - if (priv->tree == NULL) - { - grab_focus_and_unset_draw_keyfocus (tree_view); - return; - } - - if (sequence) - update_prelight (tree_view, x, y); - - /* are we in an arrow? */ - if (priv->prelight_node && - priv->arrow_prelit && - gtk_tree_view_draw_expanders (tree_view)) - { - if (button == GDK_BUTTON_PRIMARY) - { - priv->button_pressed_node = priv->prelight_node; - priv->button_pressed_tree = priv->prelight_tree; - gtk_widget_queue_draw (widget); - } - - grab_focus_and_unset_draw_keyfocus (tree_view); - gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED); - return; - } - - /* find the node that was clicked */ - new_y = TREE_WINDOW_Y_TO_RBTREE_Y(priv, bin_y); - if (new_y < 0) - new_y = 0; - y_offset = -gtk_tree_rbtree_find_offset (priv->tree, new_y, &tree, &node); - - if (node == NULL) - { - /* We clicked in dead space */ - grab_focus_and_unset_draw_keyfocus (tree_view); - return; - } - - /* Get the path and the node */ - path = _gtk_tree_path_new_from_rbtree (tree, node); - - if (row_is_separator (tree_view, NULL, path)) - { - gtk_tree_path_free (path); - grab_focus_and_unset_draw_keyfocus (tree_view); - return; - } - - depth = gtk_tree_path_get_depth (path); - background_area.y = y_offset + bin_y; - background_area.height = gtk_tree_view_get_row_height (tree_view, node); - background_area.x = 0; - - gtk_tree_view_convert_bin_window_to_widget_coords (tree_view, - background_area.x, - background_area.y, - &background_area.x, - &background_area.y); - - /* Let the column have a chance at selecting it. */ - rtl = (_gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL); - for (list = (rtl ? g_list_last (priv->columns) : g_list_first (priv->columns)); - list; list = (rtl ? list->prev : list->next)) - { - GtkTreeViewColumn *candidate = list->data; - - if (!gtk_tree_view_column_get_visible (candidate)) - continue; - - background_area.width = gtk_tree_view_column_get_width (candidate); - if ((background_area.x > x) || - (background_area.x + background_area.width <= x)) - { - background_area.x += background_area.width; - continue; - } - - /* we found the focus column */ - column = candidate; - cell_area = background_area; - cell_area.width -= _TREE_VIEW_HORIZONTAL_SEPARATOR; - cell_area.x += _TREE_VIEW_HORIZONTAL_SEPARATOR / 2; - if (gtk_tree_view_is_expander_column (tree_view, column)) - { - if (!rtl) - cell_area.x += (depth - 1) * priv->level_indentation; - cell_area.width -= (depth - 1) * priv->level_indentation; - - if (gtk_tree_view_draw_expanders (tree_view)) - { - int expander_size = gtk_tree_view_get_expander_size (tree_view); - if (!rtl) - cell_area.x += depth * expander_size; - cell_area.width -= depth * expander_size; - } - } - break; - } - - if (column == NULL) - { - gtk_tree_path_free (path); - grab_focus_and_unset_draw_keyfocus (tree_view); - gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED); - return; - } - - _gtk_tree_view_set_focus_column (tree_view, column); - - event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence); - modifiers = gdk_event_get_modifier_state (event); - - /* decide if we edit */ - if (button == GDK_BUTTON_PRIMARY && - !(modifiers & gtk_accelerator_get_default_mod_mask ())) - { - GtkTreePath *anchor; - GtkTreeIter iter; - - gtk_tree_model_get_iter (priv->model, &iter, path); - gtk_tree_view_column_cell_set_cell_data (column, - priv->model, - &iter, - GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_PARENT), - node->children?TRUE:FALSE); - - if (priv->anchor) - anchor = gtk_tree_row_reference_get_path (priv->anchor); - else - anchor = NULL; - - if ((anchor && !gtk_tree_path_compare (anchor, path)) - || !_gtk_tree_view_column_has_editable_cell (column)) - { - GtkCellEditable *cell_editable = NULL; - - /* FIXME: get the right flags */ - guint flags = 0; - - if (_gtk_tree_view_column_cell_event (column, - (GdkEvent *)event, - &cell_area, flags)) - { - GtkCellArea *area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (column)); - cell_editable = gtk_cell_area_get_edit_widget (area); - gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED); - - if (cell_editable != NULL) - { - gtk_tree_path_free (path); - gtk_tree_path_free (anchor); - return; - } - } - } - if (anchor) - gtk_tree_path_free (anchor); - } - - /* we only handle selection modifications on the first button press - */ - if (n_press == 1) - { - GtkCellRenderer *focus_cell; - gboolean modify, extend; - - get_current_selection_modifiers (GTK_EVENT_CONTROLLER (gesture), &modify, &extend); - priv->modify_selection_pressed = modify; - priv->extend_selection_pressed = extend; - - /* We update the focus cell here, this is also needed if the - * column does not contain an editable cell. In this case, - * GtkCellArea did not receive the event for processing (and - * could not update the focus cell). - */ - focus_cell = _gtk_tree_view_column_get_cell_at_pos (column, - &cell_area, - &background_area, - x, y); - - if (focus_cell) - gtk_tree_view_column_focus_cell (column, focus_cell); - - if (modify) - { - gtk_tree_view_real_set_cursor (tree_view, path, CLAMP_NODE); - gtk_tree_view_real_toggle_cursor_row (tree_view); - } - else if (extend) - { - gtk_tree_view_real_set_cursor (tree_view, path, CLAMP_NODE); - gtk_tree_view_real_select_cursor_row (tree_view, FALSE); - } - else - { - gtk_tree_view_real_set_cursor (tree_view, path, CLEAR_AND_SELECT | CLAMP_NODE); - } - - priv->modify_selection_pressed = FALSE; - priv->extend_selection_pressed = FALSE; - } - - if (button == GDK_BUTTON_PRIMARY && n_press == 2) - { - gtk_tree_view_row_activated (tree_view, path, column); - gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED); - } - else - { - if (n_press == 1) - { - priv->button_pressed_node = priv->prelight_node; - priv->button_pressed_tree = priv->prelight_tree; - } - - grab_focus_and_unset_draw_keyfocus (tree_view); - } - - gtk_tree_path_free (path); - - if (n_press >= 2) - gtk_event_controller_reset (GTK_EVENT_CONTROLLER (gesture)); -} - -static void -gtk_tree_view_drag_gesture_begin (GtkGestureDrag *gesture, - double start_x, - double start_y, - GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - int bin_x, bin_y; - GtkTreeRBTree *tree; - GtkTreeRBNode *node; - - if (priv->tree == NULL) - { - gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED); - return; - } - - gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, start_x, start_y, - &bin_x, &bin_y); - - /* Are we dragging a column header? */ - if (bin_y < 0) - return; - - priv->press_start_x = priv->rubber_band_x = bin_x; - priv->press_start_y = priv->rubber_band_y = bin_y; - gtk_tree_rbtree_find_offset (priv->tree, bin_y + priv->dy, - &tree, &node); - - if (priv->rubber_banding_enable - && !GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED) - && gtk_tree_selection_get_mode (priv->selection) == GTK_SELECTION_MULTIPLE) - { - gboolean modify, extend; - - priv->press_start_y += priv->dy; - priv->rubber_band_y += priv->dy; - priv->rubber_band_status = RUBBER_BAND_MAYBE_START; - - get_current_selection_modifiers (GTK_EVENT_CONTROLLER (gesture), &modify, &extend); - priv->rubber_band_modify = modify; - priv->rubber_band_extend = extend; - } -} - -static void -gtk_tree_view_column_click_gesture_pressed (GtkGestureClick *gesture, - int n_press, - double x, - double y, - GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreeViewColumn *column; - GList *list; - - if (n_press != 2) - return; - - for (list = priv->columns; list; list = list->next) - { - column = list->data; - - if (!_gtk_tree_view_column_coords_in_resize_rect (column, x, y) || - !gtk_tree_view_column_get_resizable (column)) - continue; - - if (gtk_tree_view_column_get_sizing (column) != GTK_TREE_VIEW_COLUMN_AUTOSIZE) - { - gtk_tree_view_column_set_fixed_width (column, -1); - gtk_tree_view_column_set_expand (column, FALSE); - _gtk_tree_view_column_autosize (tree_view, column); - } - - gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED); - break; - } -} - -static void -gtk_tree_view_column_drag_gesture_begin (GtkGestureDrag *gesture, - double start_x, - double start_y, - GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreeViewColumn *column; - gboolean rtl; - GList *list; - int i; - - rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); - - for (i = 0, list = priv->columns; list; list = list->next, i++) - { - gpointer drag_data; - int column_width; - - column = list->data; - - if (!_gtk_tree_view_column_coords_in_resize_rect (column, start_x, start_y)) - continue; - - if (!gtk_tree_view_column_get_resizable (column)) - break; - - priv->in_column_resize = TRUE; - - /* block attached dnd signal handler */ - drag_data = g_object_get_data (G_OBJECT (tree_view), "gtk-site-data"); - if (drag_data) - g_signal_handlers_block_matched (tree_view, - G_SIGNAL_MATCH_DATA, - 0, 0, NULL, NULL, - drag_data); - - column_width = gtk_tree_view_column_get_width (column); - gtk_tree_view_column_set_fixed_width (column, column_width); - gtk_tree_view_column_set_expand (column, FALSE); - - priv->drag_pos = i; - priv->x_drag = start_x + (rtl ? column_width : -column_width); - - if (!gtk_widget_has_focus (GTK_WIDGET (tree_view))) - gtk_widget_grab_focus (GTK_WIDGET (tree_view)); - - gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED); - return; - } -} - -static void -gtk_tree_view_update_button_position (GtkTreeView *tree_view, - GtkTreeViewColumn *column) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GList *column_el; - - column_el = g_list_find (priv->columns, column); - g_return_if_fail (column_el != NULL); - - gtk_css_node_insert_after (priv->header_node, - gtk_widget_get_css_node (gtk_tree_view_column_get_button (column)), - column_el->prev ? gtk_widget_get_css_node ( - gtk_tree_view_column_get_button (column_el->prev->data)) : NULL); -} - -/* column drag gesture helper */ -static gboolean -gtk_tree_view_button_release_drag_column (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkWidget *button, *widget = GTK_WIDGET (tree_view); - GList *l; - gboolean rtl; - GtkStyleContext *context; - - rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL); - - /* Move the button back */ - button = gtk_tree_view_column_get_button (priv->drag_column); - - context = gtk_widget_get_style_context (button); - gtk_style_context_remove_class (context, "dnd"); - - gtk_tree_view_update_button_position (tree_view, priv->drag_column); - gtk_widget_queue_allocate (widget); - - gtk_widget_grab_focus (button); - - if (rtl) - { - if (priv->cur_reorder && - priv->cur_reorder->right_column != priv->drag_column) - gtk_tree_view_move_column_after (tree_view, priv->drag_column, - priv->cur_reorder->right_column); - } - else - { - if (priv->cur_reorder && - priv->cur_reorder->left_column != priv->drag_column) - gtk_tree_view_move_column_after (tree_view, priv->drag_column, - priv->cur_reorder->left_column); - } - priv->drag_column = NULL; - - for (l = priv->column_drag_info; l != NULL; l = l->next) - g_slice_free (GtkTreeViewColumnReorder, l->data); - g_list_free (priv->column_drag_info); - priv->column_drag_info = NULL; - priv->cur_reorder = NULL; - - /* Reset our flags */ - priv->drag_column_surface_state = DRAG_COLUMN_WINDOW_STATE_UNSET; - priv->in_column_drag = FALSE; - - return TRUE; -} - -/* column drag gesture helper */ -static gboolean -gtk_tree_view_button_release_column_resize (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - gpointer drag_data; - - priv->drag_pos = -1; - - /* unblock attached dnd signal handler */ - drag_data = g_object_get_data (G_OBJECT (tree_view), "gtk-site-data"); - if (drag_data) - g_signal_handlers_unblock_matched (tree_view, - G_SIGNAL_MATCH_DATA, - 0, 0, NULL, NULL, - drag_data); - - priv->in_column_resize = FALSE; - return TRUE; -} - -static void -gtk_tree_view_column_drag_gesture_end (GtkGestureDrag *gesture, - double offset_x, - double offset_y, - GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GdkEventSequence *sequence; - - sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture)); - - /* Cancel reorder if the drag got cancelled */ - if (!gtk_gesture_handles_sequence (GTK_GESTURE (gesture), sequence)) - priv->cur_reorder = NULL; - - if (priv->in_column_drag) - gtk_tree_view_button_release_drag_column (tree_view); - else if (priv->in_column_resize) - gtk_tree_view_button_release_column_resize (tree_view); -} - -static void -gtk_tree_view_drag_gesture_end (GtkGestureDrag *gesture, - double offset_x, - double offset_y, - GtkTreeView *tree_view) -{ - gtk_tree_view_stop_rubber_band (tree_view); -} - -static void -gtk_tree_view_click_gesture_released (GtkGestureClick *gesture, - int n_press, - double x, - double y, - GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GdkEventSequence *sequence; - gboolean modify, extend; - guint button; - - button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture)); - sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture)); - - if (button != GDK_BUTTON_PRIMARY || - priv->button_pressed_node == NULL || - priv->button_pressed_node != priv->prelight_node) - return; - - get_current_selection_modifiers (GTK_EVENT_CONTROLLER (gesture), &modify, &extend); - - if (priv->arrow_prelit) - { - GtkTreePath *path = NULL; - - path = _gtk_tree_path_new_from_rbtree (priv->button_pressed_tree, - priv->button_pressed_node); - /* Actually activate the node */ - if (priv->button_pressed_node->children == NULL) - gtk_tree_view_real_expand_row (tree_view, path, - priv->button_pressed_tree, - priv->button_pressed_node, - FALSE); - else - gtk_tree_view_real_collapse_row (tree_view, path, - priv->button_pressed_tree, - priv->button_pressed_node); - gtk_tree_path_free (path); - } - else if (priv->activate_on_single_click && !modify && !extend) - { - GtkTreePath *path = NULL; - - path = _gtk_tree_path_new_from_rbtree (priv->button_pressed_tree, - priv->button_pressed_node); - gtk_tree_view_row_activated (tree_view, path, priv->focus_column); - gtk_tree_path_free (path); - } - - priv->button_pressed_tree = NULL; - priv->button_pressed_node = NULL; - - if (sequence) - ensure_unprelighted (tree_view); -} - -/* GtkWidget::motion_event function set. - */ - -static gboolean -coords_are_over_arrow (GtkTreeView *tree_view, - GtkTreeRBTree *tree, - GtkTreeRBNode *node, - /* these are in bin window coords */ - int x, - int y) -{ - GdkRectangle arrow; - int x2; - - if (!gtk_widget_get_realized (GTK_WIDGET (tree_view))) - return FALSE; - - if ((node->flags & GTK_TREE_RBNODE_IS_PARENT) == 0) - return FALSE; - - arrow.y = gtk_tree_view_get_row_y_offset (tree_view, tree, node); - arrow.height = gtk_tree_view_get_row_height (tree_view, node); - - gtk_tree_view_get_arrow_xrange (tree_view, tree, &arrow.x, &x2); - - arrow.width = x2 - arrow.x; - - return (x >= arrow.x && - x < (arrow.x + arrow.width) && - y >= arrow.y && - y < (arrow.y + arrow.height)); -} - -static gboolean -auto_expand_timeout (gpointer data) -{ - GtkTreeView *tree_view = GTK_TREE_VIEW (data); - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreePath *path; - - if (priv->prelight_node) - { - path = _gtk_tree_path_new_from_rbtree (priv->prelight_tree, - priv->prelight_node); - - if (priv->prelight_node->children) - gtk_tree_view_collapse_row (tree_view, path); - else - gtk_tree_view_expand_row (tree_view, path, FALSE); - - gtk_tree_path_free (path); - } - - priv->auto_expand_timeout = 0; - - return FALSE; -} - -static void -remove_auto_expand_timeout (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_clear_handle_id (&priv->auto_expand_timeout, g_source_remove); -} - -static void -do_prelight (GtkTreeView *tree_view, - GtkTreeRBTree *tree, - GtkTreeRBNode *node, - /* these are in bin_window coords */ - int x, - int y) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - if (priv->prelight_tree == tree && - priv->prelight_node == node) - { - /* We are still on the same node, - but we might need to take care of the arrow */ - - if (tree && node && gtk_tree_view_draw_expanders (tree_view)) - { - gboolean over_arrow; - - over_arrow = coords_are_over_arrow (tree_view, tree, node, x, y); - - if (over_arrow != priv->arrow_prelit) - { - if (over_arrow) - priv->arrow_prelit = TRUE; - else - priv->arrow_prelit = FALSE; - - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - } - } - - return; - } - - if (priv->prelight_tree && priv->prelight_node) - { - /* Unprelight the old node and arrow */ - - GTK_TREE_RBNODE_UNSET_FLAG (priv->prelight_node, - GTK_TREE_RBNODE_IS_PRELIT); - - if (priv->arrow_prelit - && gtk_tree_view_draw_expanders (tree_view)) - { - priv->arrow_prelit = FALSE; - - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - } - - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - } - - - if (priv->hover_expand) - remove_auto_expand_timeout (tree_view); - - /* Set the new prelight values */ - priv->prelight_node = node; - priv->prelight_tree = tree; - - if (!node || !tree) - return; - - /* Prelight the new node and arrow */ - - if (gtk_tree_view_draw_expanders (tree_view) - && coords_are_over_arrow (tree_view, tree, node, x, y)) - { - priv->arrow_prelit = TRUE; - - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - } - - GTK_TREE_RBNODE_SET_FLAG (node, GTK_TREE_RBNODE_IS_PRELIT); - - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - - if (priv->hover_expand) - { - priv->auto_expand_timeout = - g_timeout_add (AUTO_EXPAND_TIMEOUT, auto_expand_timeout, tree_view); - gdk_source_set_static_name_by_id (priv->auto_expand_timeout, "[gtk] auto_expand_timeout"); - } -} - -static void -prelight_or_select (GtkTreeView *tree_view, - GtkTreeRBTree *tree, - GtkTreeRBNode *node, - /* these are in bin_window coords */ - int x, - int y) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkSelectionMode mode = gtk_tree_selection_get_mode (priv->selection); - - if (priv->hover_selection && - (mode == GTK_SELECTION_SINGLE || mode == GTK_SELECTION_BROWSE) && - !(priv->edited_column && - gtk_cell_area_get_edit_widget - (gtk_cell_layout_get_area (GTK_CELL_LAYOUT (priv->edited_column))))) - { - if (node) - { - if (!GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED)) - { - GtkTreePath *path; - - path = _gtk_tree_path_new_from_rbtree (tree, node); - gtk_tree_selection_select_path (priv->selection, path); - if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED)) - { - priv->draw_keyfocus = FALSE; - gtk_tree_view_real_set_cursor (tree_view, path, 0); - } - gtk_tree_path_free (path); - } - } - - else if (mode == GTK_SELECTION_SINGLE) - gtk_tree_selection_unselect_all (priv->selection); - } - - do_prelight (tree_view, tree, node, x, y); -} - -static void -ensure_unprelighted (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv G_GNUC_UNUSED = gtk_tree_view_get_instance_private (tree_view); - - do_prelight (tree_view, - NULL, NULL, - -1000, -1000); /* coords not possibly over an arrow */ - - g_assert (priv->prelight_node == NULL); -} - -static void -update_prelight (GtkTreeView *tree_view, - int x, - int y) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - int new_y; - GtkTreeRBTree *tree; - GtkTreeRBNode *node; - - if (priv->tree == NULL) - return; - - if (x == -10000) - { - ensure_unprelighted (tree_view); - return; - } - - new_y = TREE_WINDOW_Y_TO_RBTREE_Y (priv, y); - if (new_y < 0) - new_y = 0; - - gtk_tree_rbtree_find_offset (priv->tree, - new_y, &tree, &node); - - if (node) - prelight_or_select (tree_view, tree, node, x, y); -} - -static gboolean -gtk_tree_view_motion_resize_column (GtkTreeView *tree_view, - double x, - double y) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - int new_width; - GtkTreeViewColumn *column; - - column = gtk_tree_view_get_column (tree_view, priv->drag_pos); - - if (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL) - new_width = MAX (priv->x_drag - x, 0); - else - new_width = MAX (x - priv->x_drag, 0); - - if (new_width != gtk_tree_view_column_get_fixed_width (column)) - gtk_tree_view_column_set_fixed_width (column, new_width); - - return FALSE; -} - -static void -gtk_tree_view_update_current_reorder (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreeViewColumnReorder *reorder = NULL; - GdkEventSequence *sequence; - GList *list; - double x; - - sequence = gtk_gesture_single_get_current_sequence - (GTK_GESTURE_SINGLE (priv->column_drag_gesture)); - gtk_gesture_get_point (priv->column_drag_gesture, - sequence, &x, NULL); - x += gtk_adjustment_get_value (priv->hadjustment); - - for (list = priv->column_drag_info; list; list = list->next) - { - reorder = (GtkTreeViewColumnReorder *) list->data; - if (x >= reorder->left_align && x < reorder->right_align) - break; - reorder = NULL; - } - - priv->cur_reorder = reorder; -} - -static void -gtk_tree_view_vertical_autoscroll (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GdkRectangle visible_rect; - int y; - int offset; - - if (gtk_gesture_is_recognized (priv->drag_gesture)) - { - GdkEventSequence *sequence; - double py; - - sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (priv->drag_gesture)); - gtk_gesture_get_point (priv->drag_gesture, sequence, NULL, &py); - gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, 0, py, - NULL, &y); - } - else - { - y = priv->event_last_y; - gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, 0, y, NULL, &y); - } - - y += priv->dy; - gtk_tree_view_get_visible_rect (tree_view, &visible_rect); - - /* see if we are near the edge. */ - offset = y - (visible_rect.y + 2 * SCROLL_EDGE_SIZE); - if (offset > 0) - { - offset = y - (visible_rect.y + visible_rect.height - 2 * SCROLL_EDGE_SIZE); - if (offset < 0) - return; - } - - gtk_adjustment_set_value (priv->vadjustment, - MAX (gtk_adjustment_get_value (priv->vadjustment) + offset, 0.0)); -} - -static void -gtk_tree_view_horizontal_autoscroll (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GdkEventSequence *sequence; - GdkRectangle visible_rect; - double x; - int offset; - - sequence = gtk_gesture_single_get_current_sequence - (GTK_GESTURE_SINGLE (priv->column_drag_gesture)); - gtk_gesture_get_point (priv->column_drag_gesture, - sequence, &x, NULL); - gtk_tree_view_get_visible_rect (tree_view, &visible_rect); - - x += gtk_adjustment_get_value (priv->hadjustment); - - /* See if we are near the edge. */ - offset = x - (visible_rect.x + SCROLL_EDGE_SIZE); - if (offset > 0) - { - offset = x - (visible_rect.x + visible_rect.width - SCROLL_EDGE_SIZE); - if (offset < 0) - return; - } - offset = offset/3; - - gtk_adjustment_set_value (priv->hadjustment, - MAX (gtk_adjustment_get_value (priv->hadjustment) + offset, 0.0)); -} - -static void -gtk_tree_view_motion_drag_column (GtkTreeView *tree_view, - double x, - double y) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreeViewColumn *column = priv->drag_column; - GtkWidget *button; - int width, button_width; - - button = gtk_tree_view_column_get_button (column); - x += gtk_adjustment_get_value (priv->hadjustment); - - /* Handle moving the header */ - width = gtk_widget_get_allocated_width (GTK_WIDGET (tree_view)); - button_width = gtk_widget_get_allocated_width (button); - priv->drag_column_x = CLAMP (x - _gtk_tree_view_column_get_drag_x (column), 0, - MAX (priv->width, width) - button_width); - - /* autoscroll, if needed */ - gtk_tree_view_horizontal_autoscroll (tree_view); - /* Update the current reorder position and arrow; */ - gtk_tree_view_update_current_reorder (tree_view); - gtk_widget_queue_allocate (GTK_WIDGET (tree_view)); -} - -static void -gtk_tree_view_stop_rubber_band (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - remove_scroll_timeout (tree_view); - - if (priv->rubber_band_status == RUBBER_BAND_ACTIVE) - { - GtkTreePath *tmp_path; - - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - - /* The anchor path should be set to the start path */ - if (priv->rubber_band_start_node) - { - tmp_path = _gtk_tree_path_new_from_rbtree (priv->rubber_band_start_tree, - priv->rubber_band_start_node); - - if (priv->anchor) - gtk_tree_row_reference_free (priv->anchor); - - priv->anchor = gtk_tree_row_reference_new (priv->model, tmp_path); - - gtk_tree_path_free (tmp_path); - } - - /* ... and the cursor to the end path */ - if (priv->rubber_band_end_node) - { - tmp_path = _gtk_tree_path_new_from_rbtree (priv->rubber_band_end_tree, - priv->rubber_band_end_node); - gtk_tree_view_real_set_cursor (GTK_TREE_VIEW (tree_view), tmp_path, 0); - gtk_tree_path_free (tmp_path); - } - - _gtk_tree_selection_emit_changed (priv->selection); - - gtk_css_node_set_parent (priv->rubber_band_cssnode, NULL); - priv->rubber_band_cssnode = NULL; - } - - /* Clear status variables */ - priv->rubber_band_status = RUBBER_BAND_OFF; - priv->rubber_band_extend = FALSE; - priv->rubber_band_modify = FALSE; - - priv->rubber_band_start_node = NULL; - priv->rubber_band_start_tree = NULL; - priv->rubber_band_end_node = NULL; - priv->rubber_band_end_tree = NULL; -} - -static void -gtk_tree_view_update_rubber_band_selection_range (GtkTreeView *tree_view, - GtkTreeRBTree *start_tree, - GtkTreeRBNode *start_node, - GtkTreeRBTree *end_tree, - GtkTreeRBNode *end_node, - gboolean select, - gboolean skip_start, - gboolean skip_end) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - if (start_node == end_node) - return; - - /* We skip the first node and jump inside the loop */ - if (skip_start) - goto skip_first; - - do - { - /* Small optimization by assuming insensitive nodes are never - * selected. - */ - if (!GTK_TREE_RBNODE_FLAG_SET (start_node, GTK_TREE_RBNODE_IS_SELECTED)) - { - GtkTreePath *path; - gboolean selectable; - - path = _gtk_tree_path_new_from_rbtree (start_tree, start_node); - selectable = _gtk_tree_selection_row_is_selectable (priv->selection, start_node, path); - gtk_tree_path_free (path); - - if (!selectable) - goto node_not_selectable; - } - - if (select) - { - if (priv->rubber_band_extend) - GTK_TREE_RBNODE_SET_FLAG (start_node, GTK_TREE_RBNODE_IS_SELECTED); - else if (priv->rubber_band_modify) - { - /* Toggle the selection state */ - if (GTK_TREE_RBNODE_FLAG_SET (start_node, GTK_TREE_RBNODE_IS_SELECTED)) - GTK_TREE_RBNODE_UNSET_FLAG (start_node, GTK_TREE_RBNODE_IS_SELECTED); - else - GTK_TREE_RBNODE_SET_FLAG (start_node, GTK_TREE_RBNODE_IS_SELECTED); - } - else - GTK_TREE_RBNODE_SET_FLAG (start_node, GTK_TREE_RBNODE_IS_SELECTED); - } - else - { - /* Mirror the above */ - if (priv->rubber_band_extend) - GTK_TREE_RBNODE_UNSET_FLAG (start_node, GTK_TREE_RBNODE_IS_SELECTED); - else if (priv->rubber_band_modify) - { - /* Toggle the selection state */ - if (GTK_TREE_RBNODE_FLAG_SET (start_node, GTK_TREE_RBNODE_IS_SELECTED)) - GTK_TREE_RBNODE_UNSET_FLAG (start_node, GTK_TREE_RBNODE_IS_SELECTED); - else - GTK_TREE_RBNODE_SET_FLAG (start_node, GTK_TREE_RBNODE_IS_SELECTED); - } - else - GTK_TREE_RBNODE_UNSET_FLAG (start_node, GTK_TREE_RBNODE_IS_SELECTED); - } - - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - -node_not_selectable: - if (start_node == end_node) - break; - -skip_first: - - if (start_node->children) - { - start_tree = start_node->children; - start_node = gtk_tree_rbtree_first (start_tree); - } - else - { - gtk_tree_rbtree_next_full (start_tree, start_node, &start_tree, &start_node); - - if (!start_tree) - /* Ran out of tree */ - break; - } - - if (skip_end && start_node == end_node) - break; - } - while (TRUE); -} - -static void -gtk_tree_view_update_rubber_band_selection (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreeRBTree *start_tree, *end_tree; - GtkTreeRBNode *start_node, *end_node; - double start_y, offset_y; - int bin_y; - - if (!gtk_gesture_is_active (priv->drag_gesture)) - return; - - gtk_gesture_drag_get_offset (GTK_GESTURE_DRAG (priv->drag_gesture), - NULL, &offset_y); - gtk_gesture_drag_get_start_point (GTK_GESTURE_DRAG (priv->drag_gesture), - NULL, &start_y); - gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, 0, start_y, - NULL, &bin_y); - bin_y = MAX (0, bin_y + offset_y + priv->dy); - - gtk_tree_rbtree_find_offset (priv->tree, MIN (priv->press_start_y, bin_y), &start_tree, &start_node); - gtk_tree_rbtree_find_offset (priv->tree, MAX (priv->press_start_y, bin_y), &end_tree, &end_node); - - /* Handle the start area first */ - if (!start_node && !end_node) - { - if (priv->rubber_band_start_node) - { - GtkTreeRBNode *node = priv->rubber_band_start_node; - - if (priv->rubber_band_modify) - { - /* Toggle the selection state */ - if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED)) - GTK_TREE_RBNODE_UNSET_FLAG (node, GTK_TREE_RBNODE_IS_SELECTED); - else - GTK_TREE_RBNODE_SET_FLAG (node, GTK_TREE_RBNODE_IS_SELECTED); - } - else - GTK_TREE_RBNODE_UNSET_FLAG (node, GTK_TREE_RBNODE_IS_SELECTED); - - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - } - } - if (!priv->rubber_band_start_node || !start_node) - { - gtk_tree_view_update_rubber_band_selection_range (tree_view, - start_tree, - start_node, - end_tree, - end_node, - TRUE, - FALSE, - FALSE); - } - else if (gtk_tree_rbtree_node_find_offset (start_tree, start_node) < - gtk_tree_rbtree_node_find_offset (priv->rubber_band_start_tree, priv->rubber_band_start_node)) - { - /* New node is above the old one; selection became bigger */ - gtk_tree_view_update_rubber_band_selection_range (tree_view, - start_tree, - start_node, - priv->rubber_band_start_tree, - priv->rubber_band_start_node, - TRUE, - FALSE, - TRUE); - } - else if (gtk_tree_rbtree_node_find_offset (start_tree, start_node) > - gtk_tree_rbtree_node_find_offset (priv->rubber_band_start_tree, priv->rubber_band_start_node)) - { - /* New node is below the old one; selection became smaller */ - gtk_tree_view_update_rubber_band_selection_range (tree_view, - priv->rubber_band_start_tree, - priv->rubber_band_start_node, - start_tree, - start_node, - FALSE, - FALSE, - TRUE); - } - - priv->rubber_band_start_tree = start_tree; - priv->rubber_band_start_node = start_node; - - /* Next, handle the end area */ - if (!priv->rubber_band_end_node) - { - /* In the event this happens, start_node was also NULL; this case is - * handled above. - */ - } - else if (!end_node) - { - /* Find the last node in the tree */ - gtk_tree_rbtree_find_offset (priv->tree, gtk_tree_view_get_height (tree_view) - 1, - &end_tree, &end_node); - - /* Selection reached end of the tree */ - gtk_tree_view_update_rubber_band_selection_range (tree_view, - priv->rubber_band_end_tree, - priv->rubber_band_end_node, - end_tree, - end_node, - TRUE, - TRUE, - FALSE); - } - else if (gtk_tree_rbtree_node_find_offset (end_tree, end_node) > - gtk_tree_rbtree_node_find_offset (priv->rubber_band_end_tree, priv->rubber_band_end_node)) - { - /* New node is below the old one; selection became bigger */ - gtk_tree_view_update_rubber_band_selection_range (tree_view, - priv->rubber_band_end_tree, - priv->rubber_band_end_node, - end_tree, - end_node, - TRUE, - TRUE, - FALSE); - } - else if (gtk_tree_rbtree_node_find_offset (end_tree, end_node) < - gtk_tree_rbtree_node_find_offset (priv->rubber_band_end_tree, priv->rubber_band_end_node)) - { - /* New node is above the old one; selection became smaller */ - gtk_tree_view_update_rubber_band_selection_range (tree_view, - end_tree, - end_node, - priv->rubber_band_end_tree, - priv->rubber_band_end_node, - FALSE, - TRUE, - FALSE); - } - - priv->rubber_band_end_tree = end_tree; - priv->rubber_band_end_node = end_node; -} - -static void -gtk_tree_view_update_rubber_band (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - double start_x, start_y, offset_x, offset_y, x, y; - int bin_x, bin_y; - - if (!gtk_gesture_is_recognized (priv->drag_gesture)) - return; - - gtk_gesture_drag_get_offset (GTK_GESTURE_DRAG (priv->drag_gesture), - &offset_x, &offset_y); - gtk_gesture_drag_get_start_point (GTK_GESTURE_DRAG (priv->drag_gesture), - &start_x, &start_y); - gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, start_x, start_y, - &bin_x, &bin_y); - bin_y += priv->dy; - - x = MAX (bin_x + offset_x, 0); - y = MAX (bin_y + offset_y, 0); - - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - - priv->rubber_band_x = x; - priv->rubber_band_y = y; - - gtk_tree_view_update_rubber_band_selection (tree_view); -} - -static void -gtk_tree_view_snapshot_rubber_band (GtkTreeView *tree_view, - GtkSnapshot *snapshot) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - double start_x, start_y, offset_x, offset_y; - GdkRectangle rect; - GtkStyleContext *context; - int bin_x, bin_y; - - if (!gtk_gesture_is_recognized (priv->drag_gesture)) - return; - - gtk_gesture_drag_get_offset (GTK_GESTURE_DRAG (priv->drag_gesture), - &offset_x, &offset_y); - gtk_gesture_drag_get_start_point (GTK_GESTURE_DRAG (priv->drag_gesture), - &start_x, &start_y); - gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, start_x, start_y, - &bin_x, &bin_y); - bin_x = MAX (0, bin_x + offset_x); - bin_y = MAX (0, bin_y + offset_y + priv->dy); - - context = gtk_widget_get_style_context (GTK_WIDGET (tree_view)); - - gtk_style_context_save_to_node (context, priv->rubber_band_cssnode); - - rect.x = MIN (priv->press_start_x, bin_x); - rect.y = MIN (priv->press_start_y, bin_y) - priv->dy; - rect.width = ABS (priv->press_start_x - bin_x) + 1; - rect.height = ABS (priv->press_start_y - bin_y) + 1; - - gtk_snapshot_render_background (snapshot, context, - rect.x, rect.y, - rect.width, rect.height); - gtk_snapshot_render_frame (snapshot, context, - rect.x, rect.y, - rect.width, rect.height); - - gtk_style_context_restore (context); -} - -static void -gtk_tree_view_column_drag_gesture_update (GtkGestureDrag *gesture, - double offset_x, - double offset_y, - GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - double start_x, start_y, x, y; - GdkEventSequence *sequence; - - sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture)); - - if (gtk_gesture_get_sequence_state (GTK_GESTURE (gesture), sequence) != GTK_EVENT_SEQUENCE_CLAIMED) - return; - - gtk_gesture_drag_get_start_point (gesture, &start_x, &start_y); - x = start_x + offset_x; - y = start_y + offset_y; - - if (priv->in_column_resize) - gtk_tree_view_motion_resize_column (tree_view, x, y); - else if (priv->in_column_drag) - gtk_tree_view_motion_drag_column (tree_view, x, y); -} - -static void -gtk_tree_view_drag_gesture_update (GtkGestureDrag *gesture, - double offset_x, - double offset_y, - GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - if (priv->tree == NULL) - { - gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED); - return; - } - - if (priv->rubber_band_status == RUBBER_BAND_MAYBE_START) - { - GtkCssNode *widget_node; - - widget_node = gtk_widget_get_css_node (GTK_WIDGET (tree_view)); - priv->rubber_band_cssnode = gtk_css_node_new (); - gtk_css_node_set_name (priv->rubber_band_cssnode, g_quark_from_static_string ("rubberband")); - gtk_css_node_set_parent (priv->rubber_band_cssnode, widget_node); - gtk_css_node_set_state (priv->rubber_band_cssnode, gtk_css_node_get_state (widget_node)); - g_object_unref (priv->rubber_band_cssnode); - - gtk_tree_view_update_rubber_band (tree_view); - - priv->rubber_band_status = RUBBER_BAND_ACTIVE; - gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED); - } - else if (priv->rubber_band_status == RUBBER_BAND_ACTIVE) - { - gtk_tree_view_update_rubber_band (tree_view); - - add_scroll_timeout (tree_view); - } - else if (!priv->rubber_band_status) - { - if (gtk_tree_view_maybe_begin_dragging_row (tree_view)) - gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED); - } -} - -static void -gtk_tree_view_motion_controller_motion (GtkEventControllerMotion *controller, - double x, - double y, - GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreeRBTree *tree; - GtkTreeRBNode *node; - int new_y; - GList *list; - gboolean cursor_set = FALSE; - - if (priv->tree) - { - int bin_x, bin_y; - - /* If we are currently pressing down a button, we don't want to prelight anything else. */ - if (gtk_gesture_is_active (priv->drag_gesture) || - gtk_gesture_is_active (priv->click_gesture)) - node = NULL; - - gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, x, y, - &bin_x, &bin_y); - new_y = MAX (0, TREE_WINDOW_Y_TO_RBTREE_Y (priv, bin_y)); - - gtk_tree_rbtree_find_offset (priv->tree, new_y, &tree, &node); - - priv->event_last_x = bin_x; - priv->event_last_y = bin_y; - prelight_or_select (tree_view, tree, node, bin_x, bin_y); - } - - for (list = priv->columns; list; list = list->next) - { - GtkTreeViewColumn *column = list->data; - - if (_gtk_tree_view_column_coords_in_resize_rect (column, x, y)) - { - gtk_widget_set_cursor_from_name (GTK_WIDGET (tree_view), "col-resize"); - cursor_set = TRUE; - break; - } - } - - if (!cursor_set) - gtk_widget_set_cursor (GTK_WIDGET (tree_view), NULL); -} - -/* Invalidate the focus rectangle near the edge of the bin_window; used when - * the tree is empty. - */ -static void -invalidate_empty_focus (GtkTreeView *tree_view) -{ - if (!gtk_widget_has_focus (GTK_WIDGET (tree_view))) - return; - - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); -} - -static void -gtk_tree_view_snapshot_grid_line (GtkTreeView *tree_view, - GtkSnapshot *snapshot, - GtkOrientation orientation, - const graphene_point_t *start, - float size) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkStyleContext *context; - const GdkRGBA *grid_line_color; - - context = gtk_widget_get_style_context (GTK_WIDGET (tree_view)); - grid_line_color = gtk_css_color_value_get_rgba (_gtk_style_context_peek_property (context, - GTK_CSS_PROPERTY_BORDER_TOP_COLOR)); - - if (!gdk_rgba_equal (grid_line_color, &priv->grid_line_color) || - (orientation == GTK_ORIENTATION_HORIZONTAL && !priv->horizontal_grid_line_texture) || - (orientation == GTK_ORIENTATION_VERTICAL && !priv->vertical_grid_line_texture)) - { - cairo_surface_t *surface; - unsigned char *data; - - g_clear_object (&priv->horizontal_grid_line_texture); - g_clear_object (&priv->vertical_grid_line_texture); - priv->grid_line_color = *grid_line_color; - - surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 2, 1); - data = cairo_image_surface_get_data (surface); - /* just color the first pixel... */ - data[0] = round (grid_line_color->blue * 255); - data[1] = round (grid_line_color->green * 255); - data[2] = round (grid_line_color->red * 255); - data[3] = round (grid_line_color->alpha * 255); - - priv->horizontal_grid_line_texture = gdk_texture_new_for_surface (surface); - cairo_surface_destroy (surface); - - surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 2); - data = cairo_image_surface_get_data (surface); - data[0] = round (grid_line_color->blue * 255); - data[1] = round (grid_line_color->green * 255); - data[2] = round (grid_line_color->red * 255); - data[3] = round (grid_line_color->alpha * 255); - - priv->vertical_grid_line_texture = gdk_texture_new_for_surface (surface); - cairo_surface_destroy (surface); - } - - g_assert (priv->horizontal_grid_line_texture); - g_assert (priv->vertical_grid_line_texture); - - if (orientation == GTK_ORIENTATION_HORIZONTAL) - { - gtk_snapshot_push_repeat (snapshot, - &GRAPHENE_RECT_INIT ( - start->x, start->y, - size, 1), - NULL); - gtk_snapshot_append_texture (snapshot, priv->horizontal_grid_line_texture, - &GRAPHENE_RECT_INIT (0, 0, 2, 1)); - gtk_snapshot_pop (snapshot); - } - else /* VERTICAL */ - { - gtk_snapshot_push_repeat (snapshot, - &GRAPHENE_RECT_INIT ( - start->x, start->y, - 1, size), - NULL); - gtk_snapshot_append_texture (snapshot, priv->vertical_grid_line_texture, - &GRAPHENE_RECT_INIT (0, 0, 1, 2)); - gtk_snapshot_pop (snapshot); - } -} - -static void -gtk_tree_view_snapshot_tree_line (GtkTreeView *tree_view, - GtkSnapshot *snapshot, - GtkOrientation orientation, - const graphene_point_t *start, - float size) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkStyleContext *context; - const GdkRGBA *tree_line_color; - - context = gtk_widget_get_style_context (GTK_WIDGET (tree_view)); - tree_line_color = gtk_css_color_value_get_rgba (_gtk_style_context_peek_property (context, - GTK_CSS_PROPERTY_BORDER_LEFT_COLOR)); - - if (!gdk_rgba_equal (tree_line_color, &priv->tree_line_color) || - (orientation == GTK_ORIENTATION_HORIZONTAL && !priv->horizontal_tree_line_texture) || - (orientation == GTK_ORIENTATION_VERTICAL && !priv->vertical_tree_line_texture)) - { - cairo_surface_t *surface; - unsigned char *data; - - g_clear_object (&priv->horizontal_tree_line_texture); - g_clear_object (&priv->vertical_tree_line_texture); - priv->tree_line_color = *tree_line_color; - - surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 2, 1); - data = cairo_image_surface_get_data (surface); - /* just color the first pixel... */ - data[0] = round (tree_line_color->blue * 255); - data[1] = round (tree_line_color->green * 255); - data[2] = round (tree_line_color->red * 255); - data[3] = round (tree_line_color->alpha * 255); - - priv->horizontal_tree_line_texture = gdk_texture_new_for_surface (surface); - cairo_surface_destroy (surface); - - surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 2); - data = cairo_image_surface_get_data (surface); - data[0] = round (tree_line_color->blue * 255); - data[1] = round (tree_line_color->green * 255); - data[2] = round (tree_line_color->red * 255); - data[3] = round (tree_line_color->alpha * 255); - - priv->vertical_tree_line_texture = gdk_texture_new_for_surface (surface); - cairo_surface_destroy (surface); - } - - g_assert (priv->horizontal_tree_line_texture); - g_assert (priv->vertical_tree_line_texture); - - if (orientation == GTK_ORIENTATION_HORIZONTAL) - { - gtk_snapshot_push_repeat (snapshot, - &GRAPHENE_RECT_INIT ( - start->x, start->y, - size, 1), - NULL); - gtk_snapshot_append_texture (snapshot, priv->horizontal_tree_line_texture, - &GRAPHENE_RECT_INIT (0, 0, 2, 1)); - gtk_snapshot_pop (snapshot); - } - else /* VERTICAL */ - { - gtk_snapshot_push_repeat (snapshot, - &GRAPHENE_RECT_INIT ( - start->x, start->y, - 1, size), - NULL); - gtk_snapshot_append_texture (snapshot, priv->vertical_tree_line_texture, - &GRAPHENE_RECT_INIT (0, 0, 1, 2)); - gtk_snapshot_pop (snapshot); - } -} - -static void -gtk_tree_view_snapshot_grid_lines (GtkTreeView *tree_view, - GtkSnapshot *snapshot) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GList *list, *first, *last; - gboolean rtl; - int current_x = 0; - int tree_view_height; - - if (priv->grid_lines != GTK_TREE_VIEW_GRID_LINES_VERTICAL && - priv->grid_lines != GTK_TREE_VIEW_GRID_LINES_BOTH) - return; - - rtl = (_gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); - - first = g_list_first (priv->columns); - last = g_list_last (priv->columns); - tree_view_height = gtk_tree_view_get_height (tree_view); - - for (list = (rtl ? last : first); - list; - list = (rtl ? list->prev : list->next)) - { - GtkTreeViewColumn *column = list->data; - - /* We don't want a line for the last column */ - if (column == (rtl ? first->data : last->data)) - break; - - if (!gtk_tree_view_column_get_visible (column)) - continue; - - current_x += gtk_tree_view_column_get_width (column); - - gtk_tree_view_snapshot_grid_line (tree_view, - snapshot, - GTK_ORIENTATION_VERTICAL, - &(graphene_point_t) { current_x - 1, 0 }, - tree_view_height); - } -} - -/* Warning: Very scary function. - * Modify at your own risk - * - * KEEP IN SYNC WITH gtk_tree_view_create_row_drag_icon()! - * FIXME: It’s not... - */ -static void -gtk_tree_view_bin_snapshot (GtkWidget *widget, - GtkSnapshot *snapshot) -{ - GtkTreeView *tree_view = GTK_TREE_VIEW (widget); - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - const int x_scroll_offset = - gtk_adjustment_get_value (priv->hadjustment); - GtkTreePath *path; - GtkTreeRBTree *tree; - GList *list; - GtkTreeRBNode *node; - GtkTreeRBNode *drag_highlight = NULL; - GtkTreeRBTree *drag_highlight_tree = NULL; - GtkTreeIter iter; - int new_y; - int y_offset, cell_offset; - int max_height; - int depth; - GdkRectangle background_area; - GdkRectangle cell_area; - GdkRectangle clip; - guint flags; - int bin_window_width; - int bin_window_height; - GtkTreePath *drag_dest_path; - GList *first_column, *last_column; - gboolean has_can_focus_cell; - gboolean rtl; - int n_visible_columns; - int expander_size; - gboolean draw_vgrid_lines, draw_hgrid_lines; - GtkStyleContext *context; - gboolean parity; - - rtl = (_gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL); - context = gtk_widget_get_style_context (widget); - - if (priv->tree == NULL) - return; - - bin_window_width = gtk_widget_get_width (GTK_WIDGET (tree_view)); - bin_window_height = gtk_widget_get_height(GTK_WIDGET (tree_view)); - - clip = (GdkRectangle) { 0, 0, bin_window_width, bin_window_height }; - new_y = TREE_WINDOW_Y_TO_RBTREE_Y (priv, clip.y); - y_offset = -gtk_tree_rbtree_find_offset (priv->tree, new_y, &tree, &node); - - if (gtk_tree_view_get_height (tree_view) < bin_window_height) - { - gtk_style_context_save (context); - gtk_style_context_add_class (context, "cell"); - - gtk_snapshot_render_background (snapshot, context, - 0, gtk_tree_view_get_height (tree_view), - bin_window_width, - bin_window_height - gtk_tree_view_get_height (tree_view)); - - gtk_style_context_restore (context); - } - - if (node == NULL) - return; - - /* find the path for the node */ - path = _gtk_tree_path_new_from_rbtree (tree, node); - gtk_tree_model_get_iter (priv->model, - &iter, - path); - depth = gtk_tree_path_get_depth (path); - gtk_tree_path_free (path); - - drag_dest_path = NULL; - - if (priv->drag_dest_row) - drag_dest_path = gtk_tree_row_reference_get_path (priv->drag_dest_row); - - if (drag_dest_path) - _gtk_tree_view_find_node (tree_view, drag_dest_path, - &drag_highlight_tree, &drag_highlight); - - draw_vgrid_lines = - priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_VERTICAL - || priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_BOTH; - draw_hgrid_lines = - priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_HORIZONTAL - || priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_BOTH; - expander_size = gtk_tree_view_get_expander_size (tree_view); - - n_visible_columns = 0; - for (list = priv->columns; list; list = list->next) - { - if (!gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (list->data))) - continue; - n_visible_columns ++; - } - - /* Find the last column */ - for (last_column = g_list_last (priv->columns); - last_column && - !(gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (last_column->data))); - last_column = last_column->prev) - ; - - /* and the first */ - for (first_column = g_list_first (priv->columns); - first_column && - !(gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (first_column->data))); - first_column = first_column->next) - ; - - /* Actually process the expose event. To do this, we want to - * start at the first node of the event, and walk the tree in - * order, drawing each successive node. - */ - - parity = !(gtk_tree_rbtree_node_get_index (tree, node) % 2); - - do - { - gboolean is_separator = FALSE; - int n_col = 0; - - parity = !parity; - is_separator = row_is_separator (tree_view, &iter, NULL); - - max_height = gtk_tree_view_get_row_height (tree_view, node); - - cell_offset = x_scroll_offset; - - background_area.y = y_offset + clip.y; - background_area.height = max_height; - - flags = 0; - - if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_PRELIT)) - flags |= GTK_CELL_RENDERER_PRELIT; - - if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED)) - flags |= GTK_CELL_RENDERER_SELECTED; - - /* we *need* to set cell data on all cells before the call - * to _has_can_focus_cell, else _has_can_focus_cell() does not - * return a correct value. - */ - for (list = (rtl ? g_list_last (priv->columns) : g_list_first (priv->columns)); - list; - list = (rtl ? list->prev : list->next)) - { - GtkTreeViewColumn *column = list->data; - gtk_tree_view_column_cell_set_cell_data (column, - priv->model, - &iter, - GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_PARENT), - node->children?TRUE:FALSE); - } - - has_can_focus_cell = gtk_tree_view_has_can_focus_cell (tree_view); - - for (list = (rtl ? g_list_last (priv->columns) : g_list_first (priv->columns)); - list; - list = (rtl ? list->prev : list->next)) - { - GtkTreeViewColumn *column = list->data; - GtkStateFlags state = 0; - int width; - gboolean draw_focus; - - if (!gtk_tree_view_column_get_visible (column)) - continue; - - n_col++; - width = gtk_tree_view_column_get_width (column); - - if (cell_offset > clip.x + clip.width || - cell_offset + width < clip.x) - { - cell_offset += width; - continue; - } - - if (gtk_tree_view_column_get_sort_indicator (column)) - flags |= GTK_CELL_RENDERER_SORTED; - else - flags &= ~GTK_CELL_RENDERER_SORTED; - - if (priv->cursor_node == node) - flags |= GTK_CELL_RENDERER_FOCUSED; - else - flags &= ~GTK_CELL_RENDERER_FOCUSED; - - if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_PARENT)) - flags |= GTK_CELL_RENDERER_EXPANDABLE; - else - flags &= ~GTK_CELL_RENDERER_EXPANDABLE; - - if (node->children) - flags |= GTK_CELL_RENDERER_EXPANDED; - else - flags &= ~GTK_CELL_RENDERER_EXPANDED; - - background_area.x = cell_offset; - background_area.width = width; - - cell_area = background_area; - cell_area.x += _TREE_VIEW_HORIZONTAL_SEPARATOR /2; - cell_area.width -= _TREE_VIEW_HORIZONTAL_SEPARATOR; - - if (draw_vgrid_lines) - { - if (list == first_column) - { - cell_area.width -= _TREE_VIEW_GRID_LINE_WIDTH / 2; - } - else if (list == last_column) - { - cell_area.x += _TREE_VIEW_GRID_LINE_WIDTH / 2; - cell_area.width -= _TREE_VIEW_GRID_LINE_WIDTH / 2; - } - else - { - cell_area.x += _TREE_VIEW_GRID_LINE_WIDTH / 2; - cell_area.width -= _TREE_VIEW_GRID_LINE_WIDTH; - } - } - - if (draw_hgrid_lines) - { - cell_area.y += _TREE_VIEW_GRID_LINE_WIDTH / 2; - cell_area.height -= _TREE_VIEW_GRID_LINE_WIDTH; - } - - if (!gdk_rectangle_intersect (&clip, &background_area, NULL)) - { - cell_offset += gtk_tree_view_column_get_width (column); - continue; - } - - background_area.x -= x_scroll_offset; - cell_area.x -= x_scroll_offset; - - gtk_tree_view_column_cell_set_cell_data (column, - priv->model, - &iter, - GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_PARENT), - node->children?TRUE:FALSE); - - gtk_style_context_save (context); - - state = gtk_cell_renderer_get_state (NULL, widget, flags); - gtk_style_context_set_state (context, state); - - gtk_style_context_add_class (context, "cell"); - - if (node == priv->cursor_node && has_can_focus_cell - && ((column == priv->focus_column - && priv->draw_keyfocus && - gtk_widget_has_visible_focus (widget)) - || (column == priv->edited_column))) - draw_focus = TRUE; - else - draw_focus = FALSE; - - /* Draw background */ - gtk_snapshot_render_background (snapshot, context, - background_area.x, - background_area.y, - background_area.width, - background_area.height); - - /* Draw frame */ - gtk_snapshot_render_frame (snapshot, context, - background_area.x, - background_area.y, - background_area.width, - background_area.height); - - if (gtk_tree_view_is_expander_column (tree_view, column)) - { - if (!rtl) - cell_area.x += (depth - 1) * priv->level_indentation; - cell_area.width -= (depth - 1) * priv->level_indentation; - - if (gtk_tree_view_draw_expanders (tree_view)) - { - if (!rtl) - cell_area.x += depth * expander_size; - cell_area.width -= depth * expander_size; - } - - if (is_separator) - { - GdkRGBA color; - - gtk_style_context_save (context); - gtk_style_context_add_class (context, "separator"); - - gtk_style_context_get_color (context, &color); - gtk_snapshot_append_color (snapshot, - &color, - &GRAPHENE_RECT_INIT( - cell_area.x, - cell_area.y + cell_area.height / 2, - cell_area.x + cell_area.width, - 1 - )); - - gtk_style_context_restore (context); - } - else - { - gtk_tree_view_column_cell_snapshot (column, - snapshot, - &background_area, - &cell_area, - flags, - draw_focus); - } - - if (gtk_tree_view_draw_expanders (tree_view) - && (node->flags & GTK_TREE_RBNODE_IS_PARENT) == GTK_TREE_RBNODE_IS_PARENT) - { - gtk_tree_view_snapshot_arrow (GTK_TREE_VIEW (widget), - snapshot, - tree, - node); - } - } - else - { - if (is_separator) - { - GdkRGBA color; - - gtk_style_context_save (context); - gtk_style_context_add_class (context, "separator"); - - gtk_style_context_get_color (context, &color); - gtk_snapshot_append_color (snapshot, - &color, - &GRAPHENE_RECT_INIT( - cell_area.x, - cell_area.y + cell_area.height / 2, - cell_area.x + cell_area.width, - 1 - )); - - gtk_style_context_restore (context); - } - else - gtk_tree_view_column_cell_snapshot (column, - snapshot, - &background_area, - &cell_area, - flags, - draw_focus); - } - - if (draw_hgrid_lines) - { - if (background_area.y >= clip.y) - gtk_tree_view_snapshot_grid_line (tree_view, - snapshot, - GTK_ORIENTATION_HORIZONTAL, - &(graphene_point_t) { - background_area.x, background_area.y - }, - background_area.width); - - if (background_area.y + max_height < clip.y + clip.height) - gtk_tree_view_snapshot_grid_line (tree_view, - snapshot, - GTK_ORIENTATION_HORIZONTAL, - &(graphene_point_t) { - background_area.x, background_area.y + max_height - }, - background_area.width); - } - - if (gtk_tree_view_is_expander_column (tree_view, column) && - priv->tree_lines_enabled) - { - int x = background_area.x; - int mult = rtl ? -1 : 1; - int y0 = background_area.y; - int y1 = background_area.y + background_area.height/2; - int y2 = background_area.y + background_area.height; - - if (rtl) - x += background_area.width - 1; - - if ((node->flags & GTK_TREE_RBNODE_IS_PARENT) == GTK_TREE_RBNODE_IS_PARENT && - depth > 1) - { - gtk_tree_view_snapshot_tree_line (tree_view, - snapshot, - GTK_ORIENTATION_HORIZONTAL, - &(graphene_point_t) { - x + expander_size * (depth - 1.5) * mult, - y1 - }, - mult * expander_size * 0.4); - - } - else if (depth > 1) - { - gtk_tree_view_snapshot_tree_line (tree_view, - snapshot, - GTK_ORIENTATION_HORIZONTAL, - &(graphene_point_t) { - x + expander_size * (depth - 1.5) * mult, - y1 - }, - mult * expander_size); - } - - if (depth > 1) - { - int i; - GtkTreeRBNode *tmp_node; - GtkTreeRBTree *tmp_tree; - - if (!gtk_tree_rbtree_next (tree, node)) - gtk_tree_view_snapshot_tree_line (tree_view, - snapshot, - GTK_ORIENTATION_VERTICAL, - &(graphene_point_t) { - x + expander_size * (depth - 1.5) * mult, - y0 - }, - y1 - y0); - else - gtk_tree_view_snapshot_tree_line (tree_view, - snapshot, - GTK_ORIENTATION_VERTICAL, - &(graphene_point_t) { - x + expander_size * (depth - 1.5) * mult, - y0 - }, - y2 - y0); - - tmp_node = tree->parent_node; - tmp_tree = tree->parent_tree; - - for (i = depth - 2; i > 0; i--) - { - if (gtk_tree_rbtree_next (tmp_tree, tmp_node)) - gtk_tree_view_snapshot_tree_line (tree_view, - snapshot, - GTK_ORIENTATION_VERTICAL, - &(graphene_point_t) { - x + expander_size * (i - 0.5) * mult, - y0 - }, - y2 - y0); - tmp_node = tmp_tree->parent_node; - tmp_tree = tmp_tree->parent_tree; - } - } - } - - gtk_style_context_restore (context); - cell_offset += gtk_tree_view_column_get_width (column); - } - - if (node == drag_highlight) - { - GtkTreeRBTree *drag_tree = NULL; - GtkTreeRBNode *drag_node = NULL; - - _gtk_tree_view_find_node (tree_view, drag_dest_path, &drag_tree, &drag_node); - if (drag_tree != NULL) - { - TreeViewDragInfo *di; - - di = get_info (tree_view); - /* Draw indicator for the drop - */ - - switch (priv->drag_dest_pos) - { - case GTK_TREE_VIEW_DROP_BEFORE: - gtk_css_node_set_classes (di->cssnode, (const char *[]){"before", NULL}); - break; - - case GTK_TREE_VIEW_DROP_AFTER: - gtk_css_node_set_classes (di->cssnode, (const char *[]){"after", NULL}); - break; - - case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: - case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: - gtk_css_node_set_classes (di->cssnode, (const char *[]){"into", NULL}); - break; - - default: - break; - } - - gtk_style_context_save_to_node (context, di->cssnode); - gtk_style_context_set_state (context, gtk_style_context_get_state (context) | GTK_STATE_FLAG_DROP_ACTIVE); - - gtk_snapshot_render_frame (snapshot, context, - 0, gtk_tree_view_get_row_y_offset (tree_view, drag_tree, drag_node), - bin_window_width, - gtk_tree_view_get_row_height (tree_view, drag_node)); - - gtk_style_context_restore (context); - } - } - - /* draw the big row-spanning focus rectangle, if needed */ - if (!has_can_focus_cell && node == priv->cursor_node && - priv->draw_keyfocus && - gtk_widget_has_visible_focus (widget)) - { - int tmp_y, tmp_height; - GtkStateFlags focus_rect_state = 0; - - gtk_style_context_save (context); - - focus_rect_state = gtk_cell_renderer_get_state (NULL, widget, flags); - gtk_style_context_set_state (context, focus_rect_state); - - if (draw_hgrid_lines) - { - tmp_y = gtk_tree_view_get_row_y_offset (tree_view, tree, node) + _TREE_VIEW_GRID_LINE_WIDTH / 2; - tmp_height = gtk_tree_view_get_row_height (tree_view, node) - _TREE_VIEW_GRID_LINE_WIDTH; - } - else - { - tmp_y = gtk_tree_view_get_row_y_offset (tree_view, tree, node); - tmp_height = gtk_tree_view_get_row_height (tree_view, node); - } - - gtk_snapshot_render_focus (snapshot, context, - 0, tmp_y, - bin_window_width, - tmp_height); - - gtk_style_context_restore (context); - } - - y_offset += max_height; - if (node->children) - { - GtkTreeIter parent = iter; - gboolean has_child; - - tree = node->children; - node = gtk_tree_rbtree_first (tree); - - has_child = gtk_tree_model_iter_children (priv->model, - &iter, - &parent); - depth++; - - /* Sanity Check! */ - TREE_VIEW_INTERNAL_ASSERT_VOID (has_child); - } - else - { - gboolean done = FALSE; - - do - { - node = gtk_tree_rbtree_next (tree, node); - if (node != NULL) - { - gboolean has_next = gtk_tree_model_iter_next (priv->model, &iter); - done = TRUE; - - /* Sanity Check! */ - TREE_VIEW_INTERNAL_ASSERT_VOID (has_next); - } - else - { - GtkTreeIter parent_iter = iter; - gboolean has_parent; - - node = tree->parent_node; - tree = tree->parent_tree; - if (tree == NULL) - /* we should go to done to free some memory */ - goto done; - has_parent = gtk_tree_model_iter_parent (priv->model, - &iter, - &parent_iter); - depth--; - - /* Sanity check */ - TREE_VIEW_INTERNAL_ASSERT_VOID (has_parent); - } - } - while (!done); - } - } - while (y_offset < clip.height); - -done: - gtk_tree_view_snapshot_grid_lines (tree_view, snapshot); - - if (priv->rubber_band_status == RUBBER_BAND_ACTIVE) - gtk_tree_view_snapshot_rubber_band (tree_view, snapshot); - - if (drag_dest_path) - gtk_tree_path_free (drag_dest_path); -} - -static void -gtk_tree_view_snapshot (GtkWidget *widget, - GtkSnapshot *snapshot) -{ - GtkTreeView *tree_view = GTK_TREE_VIEW (widget); - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkWidget *button; - GtkStyleContext *context; - GList *list; - int width, height; - - context = gtk_widget_get_style_context (widget); - width = gtk_widget_get_width (widget); - height = gtk_widget_get_height (widget); - - gtk_snapshot_push_clip (snapshot, - &GRAPHENE_RECT_INIT( - 0, gtk_tree_view_get_effective_header_height (tree_view), - width, - height - gtk_tree_view_get_effective_header_height (tree_view) - )); - - gtk_snapshot_save (snapshot); - gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT ( - - (int) gtk_adjustment_get_value (priv->hadjustment), - gtk_tree_view_get_effective_header_height (tree_view))); - gtk_tree_view_bin_snapshot (widget, snapshot); - gtk_snapshot_restore (snapshot); - - /* We can't just chain up to Container::draw as it will try to send the - * event to the headers, so we handle propagating it to our children - * (eg. widgets being edited) ourselves. - */ - for (list = priv->children; list; list = list->next) - { - GtkTreeViewChild *child = list->data; - - gtk_widget_snapshot_child (widget, child->widget, snapshot); - } - - gtk_snapshot_pop (snapshot); - - gtk_snapshot_push_clip (snapshot, - &GRAPHENE_RECT_INIT( - 0, 0, - width, - gtk_tree_view_get_effective_header_height (tree_view) - )); - - gtk_style_context_save (context); - gtk_style_context_remove_class (context, "view"); - - for (list = priv->columns; list != NULL; list = list->next) - { - GtkTreeViewColumn *column = list->data; - - if (column == priv->drag_column) - continue; - - if (gtk_tree_view_column_get_visible (column)) - { - button = gtk_tree_view_column_get_button (column); - gtk_widget_snapshot_child (widget, button, snapshot); - } - } - - if (priv->drag_column) - { - button = gtk_tree_view_column_get_button (priv->drag_column); - gtk_widget_snapshot_child (widget, button, snapshot); - } - - gtk_style_context_restore (context); - - gtk_snapshot_pop (snapshot); -} - -enum -{ - DROP_HOME, - DROP_RIGHT, - DROP_LEFT, - DROP_END -}; - -/* returns 0x1 when no column has been found -- yes it's hackish */ -static GtkTreeViewColumn * -gtk_tree_view_get_drop_column (GtkTreeView *tree_view, - GtkTreeViewColumn *column, - int drop_position) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreeViewColumn *left_column = NULL; - GtkTreeViewColumn *cur_column = NULL; - GList *tmp_list; - - if (!gtk_tree_view_column_get_reorderable (column)) - return (GtkTreeViewColumn *)0x1; - - switch (drop_position) - { - case DROP_HOME: - /* find first column where we can drop */ - tmp_list = priv->columns; - if (column == GTK_TREE_VIEW_COLUMN (tmp_list->data)) - return (GtkTreeViewColumn *)0x1; - - while (tmp_list) - { - g_assert (tmp_list); - - cur_column = GTK_TREE_VIEW_COLUMN (tmp_list->data); - tmp_list = tmp_list->next; - - if (left_column && - gtk_tree_view_column_get_visible (left_column) == FALSE) - continue; - - if (!priv->column_drop_func) - return left_column; - - if (!priv->column_drop_func (tree_view, column, left_column, cur_column, priv->column_drop_func_data)) - { - left_column = cur_column; - continue; - } - - return left_column; - } - - if (!priv->column_drop_func) - return left_column; - - if (priv->column_drop_func (tree_view, column, left_column, NULL, priv->column_drop_func_data)) - return left_column; - else - return (GtkTreeViewColumn *)0x1; - break; - - case DROP_RIGHT: - /* find first column after column where we can drop */ - tmp_list = priv->columns; - - for (; tmp_list; tmp_list = tmp_list->next) - if (GTK_TREE_VIEW_COLUMN (tmp_list->data) == column) - break; - - if (!tmp_list || !tmp_list->next) - return (GtkTreeViewColumn *)0x1; - - tmp_list = tmp_list->next; - left_column = GTK_TREE_VIEW_COLUMN (tmp_list->data); - tmp_list = tmp_list->next; - - while (tmp_list) - { - g_assert (tmp_list); - - cur_column = GTK_TREE_VIEW_COLUMN (tmp_list->data); - tmp_list = tmp_list->next; - - if (left_column && - gtk_tree_view_column_get_visible (left_column) == FALSE) - { - left_column = cur_column; - if (tmp_list) - tmp_list = tmp_list->next; - continue; - } - - if (!priv->column_drop_func) - return left_column; - - if (!priv->column_drop_func (tree_view, column, left_column, cur_column, priv->column_drop_func_data)) - { - left_column = cur_column; - continue; - } - - return left_column; - } - - if (!priv->column_drop_func) - return left_column; - - if (priv->column_drop_func (tree_view, column, left_column, NULL, priv->column_drop_func_data)) - return left_column; - else - return (GtkTreeViewColumn *)0x1; - break; - - case DROP_LEFT: - /* find first column before column where we can drop */ - tmp_list = priv->columns; - - for (; tmp_list; tmp_list = tmp_list->next) - if (GTK_TREE_VIEW_COLUMN (tmp_list->data) == column) - break; - - if (!tmp_list || !tmp_list->prev) - return (GtkTreeViewColumn *)0x1; - - tmp_list = tmp_list->prev; - cur_column = GTK_TREE_VIEW_COLUMN (tmp_list->data); - tmp_list = tmp_list->prev; - - while (tmp_list) - { - g_assert (tmp_list); - - left_column = GTK_TREE_VIEW_COLUMN (tmp_list->data); - - if (left_column && - gtk_tree_view_column_get_visible (left_column) == FALSE) - { - /*if (!tmp_list->prev) - return (GtkTreeViewColumn *)0x1; - */ -/* - cur_column = GTK_TREE_VIEW_COLUMN (tmp_list->prev->data); - tmp_list = tmp_list->prev->prev; - continue;*/ - - cur_column = left_column; - if (tmp_list) - tmp_list = tmp_list->prev; - continue; - } - - if (!priv->column_drop_func) - return left_column; - - if (priv->column_drop_func (tree_view, column, left_column, cur_column, priv->column_drop_func_data)) - return left_column; - - cur_column = left_column; - tmp_list = tmp_list->prev; - } - - if (!priv->column_drop_func) - return NULL; - - if (priv->column_drop_func (tree_view, column, NULL, cur_column, priv->column_drop_func_data)) - return NULL; - else - return (GtkTreeViewColumn *)0x1; - break; - - case DROP_END: - /* same as DROP_HOME case, but doing it backwards */ - tmp_list = g_list_last (priv->columns); - cur_column = NULL; - - if (column == GTK_TREE_VIEW_COLUMN (tmp_list->data)) - return (GtkTreeViewColumn *)0x1; - - while (tmp_list) - { - g_assert (tmp_list); - - left_column = GTK_TREE_VIEW_COLUMN (tmp_list->data); - - if (left_column && - gtk_tree_view_column_get_visible (left_column) == FALSE) - { - cur_column = left_column; - tmp_list = tmp_list->prev; - } - - if (!priv->column_drop_func) - return left_column; - - if (priv->column_drop_func (tree_view, column, left_column, cur_column, priv->column_drop_func_data)) - return left_column; - - cur_column = left_column; - tmp_list = tmp_list->prev; - } - - if (!priv->column_drop_func) - return NULL; - - if (priv->column_drop_func (tree_view, column, NULL, cur_column, priv->column_drop_func_data)) - return NULL; - else - return (GtkTreeViewColumn *)0x1; - break; - - default: - return (GtkTreeViewColumn *)0x1; - break; - } -} - -static gboolean -gtk_tree_view_search_key_cancels_search (guint keyval) -{ - return keyval == GDK_KEY_Escape - || keyval == GDK_KEY_Tab - || keyval == GDK_KEY_KP_Tab - || keyval == GDK_KEY_ISO_Left_Tab; -} - -static gboolean -gtk_tree_view_key_controller_key_pressed (GtkEventControllerKey *key, - guint keyval, - guint keycode, - GdkModifierType state, - GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkWidget *widget = GTK_WIDGET (tree_view); - GtkWidget *button; - - if (priv->rubber_band_status) - { - if (keyval == GDK_KEY_Escape) - gtk_tree_view_stop_rubber_band (tree_view); - - return TRUE; - } - - if (priv->in_column_drag) - { - if (keyval == GDK_KEY_Escape) - gtk_gesture_set_state (GTK_GESTURE (priv->column_drag_gesture), - GTK_EVENT_SEQUENCE_DENIED); - return TRUE; - } - - if (priv->headers_visible) - { - GList *focus_column; - gboolean rtl; - - rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); - - for (focus_column = priv->columns; - focus_column; - focus_column = focus_column->next) - { - GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN (focus_column->data); - - button = gtk_tree_view_column_get_button (column); - if (gtk_widget_has_focus (button)) - break; - } - - if (focus_column && - (state & GDK_SHIFT_MASK) && (state & GDK_ALT_MASK) && - (keyval == GDK_KEY_Left || keyval == GDK_KEY_KP_Left - || keyval == GDK_KEY_Right || keyval == GDK_KEY_KP_Right)) - { - GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN (focus_column->data); - int column_width; - - if (!gtk_tree_view_column_get_resizable (column)) - { - gtk_widget_error_bell (widget); - return TRUE; - } - - column_width = gtk_tree_view_column_get_width (column); - - if (keyval == (rtl ? GDK_KEY_Right : GDK_KEY_Left) - || keyval == (rtl ? GDK_KEY_KP_Right : GDK_KEY_KP_Left)) - { - column_width = MAX (column_width - 2, 0); - } - else if (keyval == (rtl ? GDK_KEY_Left : GDK_KEY_Right) - || keyval == (rtl ? GDK_KEY_KP_Left : GDK_KEY_KP_Right)) - { - column_width = column_width + 2; - } - - gtk_tree_view_column_set_fixed_width (column, column_width); - gtk_tree_view_column_set_expand (column, FALSE); - return TRUE; - } - - if (focus_column && - (state & GDK_ALT_MASK) && - (keyval == GDK_KEY_Left || keyval == GDK_KEY_KP_Left - || keyval == GDK_KEY_Right || keyval == GDK_KEY_KP_Right - || keyval == GDK_KEY_Home || keyval == GDK_KEY_KP_Home - || keyval == GDK_KEY_End || keyval == GDK_KEY_KP_End)) - { - GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN (focus_column->data); - - if (keyval == (rtl ? GDK_KEY_Right : GDK_KEY_Left) - || keyval == (rtl ? GDK_KEY_KP_Right : GDK_KEY_KP_Left)) - { - GtkTreeViewColumn *col; - col = gtk_tree_view_get_drop_column (tree_view, column, DROP_LEFT); - if (col != (GtkTreeViewColumn *)0x1) - gtk_tree_view_move_column_after (tree_view, column, col); - else - gtk_widget_error_bell (widget); - } - else if (keyval == (rtl ? GDK_KEY_Left : GDK_KEY_Right) - || keyval == (rtl ? GDK_KEY_KP_Left : GDK_KEY_KP_Right)) - { - GtkTreeViewColumn *col; - col = gtk_tree_view_get_drop_column (tree_view, column, DROP_RIGHT); - if (col != (GtkTreeViewColumn *)0x1) - gtk_tree_view_move_column_after (tree_view, column, col); - else - gtk_widget_error_bell (widget); - } - else if (keyval == GDK_KEY_Home || keyval == GDK_KEY_KP_Home) - { - GtkTreeViewColumn *col; - col = gtk_tree_view_get_drop_column (tree_view, column, DROP_HOME); - if (col != (GtkTreeViewColumn *)0x1) - gtk_tree_view_move_column_after (tree_view, column, col); - else - gtk_widget_error_bell (widget); - } - else if (keyval == GDK_KEY_End || keyval == GDK_KEY_KP_End) - { - GtkTreeViewColumn *col; - col = gtk_tree_view_get_drop_column (tree_view, column, DROP_END); - if (col != (GtkTreeViewColumn *)0x1) - gtk_tree_view_move_column_after (tree_view, column, col); - else - gtk_widget_error_bell (widget); - } - - return TRUE; - } - } - - return FALSE; -} - -static gboolean -gtk_tree_view_forward_controller_key_pressed (GtkEventControllerKey *key, - guint keyval, - guint keycode, - GdkModifierType state, - GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - if (priv->search_entry_avoid_unhandled_binding) - { - priv->search_entry_avoid_unhandled_binding = FALSE; - return FALSE; - } - - /* Initially, before the search window is visible, we pass the event to the - * IM context of the search entry box. If it triggers a commit or a preedit, - * we then show the search window without losing tree view focus. - * If the search window is already visible, we forward the events to it, - * keeping the focus on the tree view. - */ - if (gtk_widget_has_focus (GTK_WIDGET (tree_view)) - && priv->enable_search - && !priv->search_custom_entry_set - && !gtk_tree_view_search_key_cancels_search (keyval)) - { - gtk_tree_view_ensure_interactive_directory (tree_view); - - if (!gtk_widget_is_visible (priv->search_popover)) - { - priv->imcontext_changed = FALSE; - - gtk_event_controller_key_forward (key, priv->search_entry); - - if (priv->imcontext_changed) - return gtk_tree_view_real_start_interactive_search (tree_view, FALSE); - } - } - - return FALSE; -} - -static void -gtk_tree_view_key_controller_key_released (GtkEventControllerKey *key, - guint keyval, - guint keycode, - GdkModifierType state, - GtkTreeView *tree_view) -{ -} - -static void -gtk_tree_view_motion_controller_enter (GtkEventControllerMotion *controller, - double x, - double y, - GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreeRBTree *tree; - GtkTreeRBNode *node; - int new_y; - - if (priv->tree == NULL) - return; - - /* find the node internally */ - new_y = TREE_WINDOW_Y_TO_RBTREE_Y(priv, y); - if (new_y < 0) - new_y = 0; - gtk_tree_rbtree_find_offset (priv->tree, new_y, &tree, &node); - - priv->event_last_x = x; - priv->event_last_y = y; - - if ((priv->button_pressed_node == NULL) || - (priv->button_pressed_node == node)) - prelight_or_select (tree_view, tree, node, x, y); -} - -static void -gtk_tree_view_motion_controller_leave (GtkEventControllerMotion *controller, - GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - if (priv->prelight_node) - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - - priv->event_last_x = -10000; - priv->event_last_y = -10000; - - prelight_or_select (tree_view, NULL, NULL, -1000, -1000); /* not possibly over an arrow */ -} - -static void -gtk_tree_view_focus_controller_focus_out (GtkEventController *focus, - GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - - if (priv->search_popover && - !gtk_event_controller_focus_contains_focus (GTK_EVENT_CONTROLLER_FOCUS (focus))) - gtk_tree_view_search_popover_hide (priv->search_popover, tree_view); -} - -/* Incremental Reflow - */ - -static int -get_separator_height (GtkTreeView *tree_view) -{ - GtkStyleContext *context; - GtkCssStyle *style; - double d; - int min_size; - - context = gtk_widget_get_style_context (GTK_WIDGET (tree_view)); - gtk_style_context_save (context); - gtk_style_context_add_class (context, "separator"); - - style = gtk_style_context_lookup_style (context); - d = _gtk_css_number_value_get (style->size->min_height, 100); - - if (d < 1) - min_size = ceil (d); - else - min_size = floor (d); - - gtk_style_context_restore (context); - - return min_size; -} - -/* Returns TRUE if it updated the size - */ -static gboolean -validate_row (GtkTreeView *tree_view, - GtkTreeRBTree *tree, - GtkTreeRBNode *node, - GtkTreeIter *iter, - GtkTreePath *path) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreeViewColumn *column; - GtkStyleContext *context; - GList *list, *first_column, *last_column; - int height = 0; - int depth = gtk_tree_path_get_depth (path); - gboolean retval = FALSE; - gboolean is_separator = FALSE; - gboolean draw_vgrid_lines, draw_hgrid_lines; - int expander_size; - int separator_height; - - /* double check the row needs validating */ - if (! GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_INVALID) && - ! GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_COLUMN_INVALID)) - return FALSE; - - is_separator = row_is_separator (tree_view, iter, NULL); - - draw_vgrid_lines = - priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_VERTICAL - || priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_BOTH; - draw_hgrid_lines = - priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_HORIZONTAL - || priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_BOTH; - expander_size = gtk_tree_view_get_expander_size (tree_view); - - for (last_column = g_list_last (priv->columns); - last_column && - !(gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (last_column->data))); - last_column = last_column->prev) - ; - - for (first_column = g_list_first (priv->columns); - first_column && - !(gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (first_column->data))); - first_column = first_column->next) - ; - - separator_height = get_separator_height (tree_view); - - context = gtk_widget_get_style_context (GTK_WIDGET (tree_view)); - gtk_style_context_save (context); - gtk_style_context_add_class (context, "cell"); - - for (list = priv->columns; list; list = list->next) - { - int padding = 0; - int original_width; - int new_width; - int row_height; - - column = list->data; - - if (!gtk_tree_view_column_get_visible (column)) - continue; - - if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_COLUMN_INVALID) && - !_gtk_tree_view_column_cell_get_dirty (column)) - continue; - - original_width = _gtk_tree_view_column_get_requested_width (column); - - gtk_tree_view_column_cell_set_cell_data (column, priv->model, iter, - GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_PARENT), - node->children?TRUE:FALSE); - gtk_tree_view_column_cell_get_size (column, - NULL, NULL, - NULL, &row_height); - - if (is_separator) - { - height = separator_height; - /* gtk_tree_view_get_row_height() assumes separator nodes are > 0 */ - height = MAX (height, 1); - } - else - { - height = MAX (height, row_height); - height = MAX (height, expander_size); - } - - if (gtk_tree_view_is_expander_column (tree_view, column)) - { - padding += _TREE_VIEW_HORIZONTAL_SEPARATOR + (depth - 1) * priv->level_indentation; - - if (gtk_tree_view_draw_expanders (tree_view)) - padding += depth * expander_size; - } - else - padding += _TREE_VIEW_HORIZONTAL_SEPARATOR; - - if (draw_vgrid_lines) - { - if (list->data == first_column || list->data == last_column) - padding += _TREE_VIEW_GRID_LINE_WIDTH / 2.0; - else - padding += _TREE_VIEW_GRID_LINE_WIDTH; - } - - /* Update the padding for the column */ - _gtk_tree_view_column_push_padding (column, padding); - new_width = _gtk_tree_view_column_get_requested_width (column); - - if (new_width > original_width) - retval = TRUE; - } - - gtk_style_context_restore (context); - - if (draw_hgrid_lines) - height += _TREE_VIEW_GRID_LINE_WIDTH; - - if (height != GTK_TREE_RBNODE_GET_HEIGHT (node)) - { - retval = TRUE; - gtk_tree_rbtree_node_set_height (tree, node, height); - } - gtk_tree_rbtree_node_mark_valid (tree, node); - - return retval; -} - - -static void -validate_visible_area (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreePath *path = NULL; - GtkTreePath *above_path = NULL; - GtkTreeIter iter; - GtkTreeRBTree *tree = NULL; - GtkTreeRBNode *node = NULL; - gboolean need_redraw = FALSE; - gboolean size_changed = FALSE; - int total_height; - int area_above = 0; - int area_below = 0; - - if (priv->tree == NULL) - return; - - if (! GTK_TREE_RBNODE_FLAG_SET (priv->tree->root, GTK_TREE_RBNODE_DESCENDANTS_INVALID) && - priv->scroll_to_path == NULL) - return; - - total_height = gtk_widget_get_height (GTK_WIDGET (tree_view)) - - gtk_tree_view_get_effective_header_height (tree_view); - - if (total_height == 0) - return; - - /* First, we check to see if we need to scroll anywhere - */ - if (priv->scroll_to_path) - { - path = gtk_tree_row_reference_get_path (priv->scroll_to_path); - if (path && !_gtk_tree_view_find_node (tree_view, path, &tree, &node)) - { - /* we are going to scroll, and will update dy */ - gtk_tree_model_get_iter (priv->model, &iter, path); - if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_INVALID) || - GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_COLUMN_INVALID)) - { - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - if (validate_row (tree_view, tree, node, &iter, path)) - size_changed = TRUE; - } - - if (priv->scroll_to_use_align) - { - int height = gtk_tree_view_get_row_height (tree_view, node); - area_above = (total_height - height) * - priv->scroll_to_row_align; - area_below = total_height - area_above - height; - area_above = MAX (area_above, 0); - area_below = MAX (area_below, 0); - } - else - { - /* two cases: - * 1) row not visible - * 2) row visible - */ - int dy; - int height = gtk_tree_view_get_row_height (tree_view, node); - - dy = gtk_tree_rbtree_node_find_offset (tree, node); - - if (dy >= gtk_adjustment_get_value (priv->vadjustment) && - dy + height <= (gtk_adjustment_get_value (priv->vadjustment) - + gtk_adjustment_get_page_size (priv->vadjustment))) - { - /* row visible: keep the row at the same position */ - area_above = dy - gtk_adjustment_get_value (priv->vadjustment); - area_below = (gtk_adjustment_get_value (priv->vadjustment) + - gtk_adjustment_get_page_size (priv->vadjustment)) - - dy - height; - } - else - { - /* row not visible */ - if (dy >= 0 - && dy + height <= gtk_adjustment_get_page_size (priv->vadjustment)) - { - /* row at the beginning -- fixed */ - area_above = dy; - area_below = gtk_adjustment_get_page_size (priv->vadjustment) - - area_above - height; - } - else if (dy >= (gtk_adjustment_get_upper (priv->vadjustment) - - gtk_adjustment_get_page_size (priv->vadjustment))) - { - /* row at the end -- fixed */ - area_above = dy - (gtk_adjustment_get_upper (priv->vadjustment) - - gtk_adjustment_get_page_size (priv->vadjustment)); - area_below = gtk_adjustment_get_page_size (priv->vadjustment) - - area_above - height; - - if (area_below < 0) - { - area_above = gtk_adjustment_get_page_size (priv->vadjustment) - height; - area_below = 0; - } - } - else - { - /* row somewhere in the middle, bring it to the top - * of the view - */ - area_above = 0; - area_below = total_height - height; - } - } - } - } - else - /* the scroll to isn't valid; ignore it. - */ - { - if (priv->scroll_to_path && !path) - { - gtk_tree_row_reference_free (priv->scroll_to_path); - priv->scroll_to_path = NULL; - } - if (path) - gtk_tree_path_free (path); - path = NULL; - } - } - - /* We didn't have a scroll_to set, so we just handle things normally - */ - if (path == NULL) - { - int offset; - - offset = gtk_tree_rbtree_find_offset (priv->tree, - TREE_WINDOW_Y_TO_RBTREE_Y (priv, 0), - &tree, &node); - if (node == NULL) - { - /* In this case, nothing has been validated */ - path = gtk_tree_path_new_first (); - _gtk_tree_view_find_node (tree_view, path, &tree, &node); - } - else - { - path = _gtk_tree_path_new_from_rbtree (tree, node); - total_height += offset; - } - - gtk_tree_model_get_iter (priv->model, &iter, path); - - if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_INVALID) || - GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_COLUMN_INVALID)) - { - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - if (validate_row (tree_view, tree, node, &iter, path)) - size_changed = TRUE; - } - area_above = 0; - area_below = total_height - gtk_tree_view_get_row_height (tree_view, node); - } - - above_path = gtk_tree_path_copy (path); - - /* if we do not validate any row above the new top_row, we will make sure - * that the row immediately above top_row has been validated. (if we do not - * do this, gtk_tree_rbtree_find_offset will find the row above top_row, because - * when invalidated that row's height will be zero. and this will mess up - * scrolling). - */ - if (area_above == 0) - { - GtkTreeRBTree *tmptree; - GtkTreeRBNode *tmpnode; - - _gtk_tree_view_find_node (tree_view, above_path, &tmptree, &tmpnode); - gtk_tree_rbtree_prev_full (tmptree, tmpnode, &tmptree, &tmpnode); - - if (tmpnode) - { - GtkTreePath *tmppath; - GtkTreeIter tmpiter; - - tmppath = _gtk_tree_path_new_from_rbtree (tmptree, tmpnode); - gtk_tree_model_get_iter (priv->model, &tmpiter, tmppath); - - if (GTK_TREE_RBNODE_FLAG_SET (tmpnode, GTK_TREE_RBNODE_INVALID) || - GTK_TREE_RBNODE_FLAG_SET (tmpnode, GTK_TREE_RBNODE_COLUMN_INVALID)) - { - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - if (validate_row (tree_view, tmptree, tmpnode, &tmpiter, tmppath)) - size_changed = TRUE; - } - - gtk_tree_path_free (tmppath); - } - } - - /* Now, we walk forwards and backwards, measuring rows. Unfortunately, - * backwards is much slower then forward, as there is no iter_prev function. - * We go forwards first in case we run out of tree. Then we go backwards to - * fill out the top. - */ - while (node && area_below > 0) - { - if (node->children) - { - GtkTreeIter parent = iter; - gboolean has_child; - - tree = node->children; - node = gtk_tree_rbtree_first (tree); - - has_child = gtk_tree_model_iter_children (priv->model, - &iter, - &parent); - TREE_VIEW_INTERNAL_ASSERT_VOID (has_child); - gtk_tree_path_down (path); - } - else - { - gboolean done = FALSE; - do - { - node = gtk_tree_rbtree_next (tree, node); - if (node != NULL) - { - gboolean has_next = gtk_tree_model_iter_next (priv->model, &iter); - done = TRUE; - gtk_tree_path_next (path); - - /* Sanity Check! */ - TREE_VIEW_INTERNAL_ASSERT_VOID (has_next); - } - else - { - GtkTreeIter parent_iter = iter; - gboolean has_parent; - - node = tree->parent_node; - tree = tree->parent_tree; - if (tree == NULL) - break; - has_parent = gtk_tree_model_iter_parent (priv->model, - &iter, - &parent_iter); - gtk_tree_path_up (path); - - /* Sanity check */ - TREE_VIEW_INTERNAL_ASSERT_VOID (has_parent); - } - } - while (!done); - } - - if (!node) - break; - - if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_INVALID) || - GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_COLUMN_INVALID)) - { - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - if (validate_row (tree_view, tree, node, &iter, path)) - size_changed = TRUE; - } - - area_below -= gtk_tree_view_get_row_height (tree_view, node); - } - gtk_tree_path_free (path); - - /* If we ran out of tree, and have extra area_below left, we need to add it - * to area_above */ - if (area_below > 0) - area_above += area_below; - - _gtk_tree_view_find_node (tree_view, above_path, &tree, &node); - - /* We walk backwards */ - while (area_above > 0) - { - gtk_tree_rbtree_prev_full (tree, node, &tree, &node); - - /* Always find the new path in the tree. We cannot just assume - * a gtk_tree_path_prev() is enough here, as there might be children - * in between this node and the previous sibling node. If this - * appears to be a performance hotspot in profiles, we can look into - * intrigate logic for keeping path, node and iter in sync like - * we do for forward walks. (Which will be hard because of the lacking - * iter_prev). - */ - - if (node == NULL) - break; - - gtk_tree_path_free (above_path); - above_path = _gtk_tree_path_new_from_rbtree (tree, node); - - gtk_tree_model_get_iter (priv->model, &iter, above_path); - - if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_INVALID) || - GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_COLUMN_INVALID)) - { - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - if (validate_row (tree_view, tree, node, &iter, above_path)) - size_changed = TRUE; - } - area_above -= gtk_tree_view_get_row_height (tree_view, node); - } - - /* if we scrolled to a path, we need to set the dy here, - * and sync the top row accordingly - */ - if (priv->scroll_to_path) - { - gtk_tree_view_set_top_row (tree_view, above_path, -area_above); - gtk_tree_view_top_row_to_dy (tree_view); - - need_redraw = TRUE; - } - else if (gtk_tree_view_get_height (tree_view) <= gtk_adjustment_get_page_size (priv->vadjustment)) - { - /* when we are not scrolling, we should never set dy to something - * else than zero. we update top_row to be in sync with dy = 0. - */ - gtk_adjustment_set_value (GTK_ADJUSTMENT (priv->vadjustment), 0); - gtk_tree_view_dy_to_top_row (tree_view); - } - else if (gtk_adjustment_get_value (priv->vadjustment) + gtk_adjustment_get_page_size (priv->vadjustment) > gtk_tree_view_get_height (tree_view)) - { - gtk_adjustment_set_value (GTK_ADJUSTMENT (priv->vadjustment), gtk_tree_view_get_height (tree_view) - gtk_adjustment_get_page_size (priv->vadjustment)); - gtk_tree_view_dy_to_top_row (tree_view); - } - else - gtk_tree_view_top_row_to_dy (tree_view); - - /* update width/height and queue a resize */ - if (size_changed) - { - GtkRequisition requisition; - - /* We temporarily guess a size, under the assumption that it will be the - * same when we get our next size_allocate. If we don't do this, we'll be - * in an inconsistent state if we call top_row_to_dy. */ - - gtk_widget_get_preferred_size (GTK_WIDGET (tree_view), - &requisition, NULL); - gtk_adjustment_set_upper (priv->hadjustment, - MAX (gtk_adjustment_get_upper (priv->hadjustment), requisition.width)); - gtk_adjustment_set_upper (priv->vadjustment, - MAX (gtk_adjustment_get_upper (priv->vadjustment), requisition.height)); - gtk_widget_queue_resize (GTK_WIDGET (tree_view)); - } - - if (priv->scroll_to_path) - { - gtk_tree_row_reference_free (priv->scroll_to_path); - priv->scroll_to_path = NULL; - } - - if (above_path) - gtk_tree_path_free (above_path); - - if (priv->scroll_to_column) - { - priv->scroll_to_column = NULL; - } - if (need_redraw) - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); -} - -static void -initialize_fixed_height_mode (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - if (!priv->tree) - return; - - if (priv->fixed_height < 0) - { - GtkTreeIter iter; - GtkTreePath *path; - - GtkTreeRBTree *tree = NULL; - GtkTreeRBNode *node = NULL; - - tree = priv->tree; - node = tree->root; - - path = _gtk_tree_path_new_from_rbtree (tree, node); - gtk_tree_model_get_iter (priv->model, &iter, path); - - validate_row (tree_view, tree, node, &iter, path); - - gtk_tree_path_free (path); - - priv->fixed_height = gtk_tree_view_get_row_height (tree_view, node); - } - - gtk_tree_rbtree_set_fixed_height (priv->tree, - priv->fixed_height, TRUE); -} - -/* Our strategy for finding nodes to validate is a little convoluted. We find - * the left-most uninvalidated node. We then try walking right, validating - * nodes. Once we find a valid node, we repeat the previous process of finding - * the first invalid node. - */ - -static gboolean -do_validate_rows (GtkTreeView *tree_view, gboolean queue_resize) -{ - static gboolean prevent_recursion_hack = FALSE; - - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreeRBTree *tree = NULL; - GtkTreeRBNode *node = NULL; - gboolean validated_area = FALSE; - int retval = TRUE; - GtkTreePath *path = NULL; - GtkTreeIter iter; - GTimer *timer; - int i = 0; - - int y = -1; - int prev_height = -1; - gboolean fixed_height = TRUE; - - g_assert (tree_view); - - /* prevent infinite recursion via get_preferred_width() */ - if (prevent_recursion_hack) - return FALSE; - - if (priv->tree == NULL) - return FALSE; - - if (priv->fixed_height_mode) - { - if (priv->fixed_height < 0) - initialize_fixed_height_mode (tree_view); - - return FALSE; - } - - timer = g_timer_new (); - g_timer_start (timer); - - do - { - gboolean changed = FALSE; - - if (! GTK_TREE_RBNODE_FLAG_SET (priv->tree->root, GTK_TREE_RBNODE_DESCENDANTS_INVALID)) - { - retval = FALSE; - goto done; - } - - if (path != NULL) - { - node = gtk_tree_rbtree_next (tree, node); - if (node != NULL) - { - TREE_VIEW_INTERNAL_ASSERT (gtk_tree_model_iter_next (priv->model, &iter), FALSE); - gtk_tree_path_next (path); - } - else - { - gtk_tree_path_free (path); - path = NULL; - } - } - - if (path == NULL) - { - tree = priv->tree; - node = priv->tree->root; - - g_assert (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_DESCENDANTS_INVALID)); - - do - { - if (!gtk_tree_rbtree_is_nil (node->left) && - GTK_TREE_RBNODE_FLAG_SET (node->left, GTK_TREE_RBNODE_DESCENDANTS_INVALID)) - { - node = node->left; - } - else if (!gtk_tree_rbtree_is_nil (node->right) && - GTK_TREE_RBNODE_FLAG_SET (node->right, GTK_TREE_RBNODE_DESCENDANTS_INVALID)) - { - node = node->right; - } - else if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_INVALID) || - GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_COLUMN_INVALID)) - { - break; - } - else if (node->children != NULL) - { - tree = node->children; - node = tree->root; - } - else - /* RBTree corruption! All bad */ - g_assert_not_reached (); - } - while (TRUE); - path = _gtk_tree_path_new_from_rbtree (tree, node); - gtk_tree_model_get_iter (priv->model, &iter, path); - } - - changed = validate_row (tree_view, tree, node, &iter, path); - validated_area = changed || validated_area; - - if (changed) - { - int offset = gtk_tree_view_get_row_y_offset (tree_view, tree, node); - - if (y == -1 || y > offset) - y = offset; - } - - if (!priv->fixed_height_check) - { - int height; - - height = gtk_tree_view_get_row_height (tree_view, node); - if (prev_height < 0) - prev_height = height; - else if (prev_height != height) - fixed_height = FALSE; - } - - i++; - } - while (g_timer_elapsed (timer, NULL) < GTK_TREE_VIEW_TIME_MS_PER_IDLE / 1000.); - - if (!priv->fixed_height_check) - { - if (fixed_height) - gtk_tree_rbtree_set_fixed_height (priv->tree, prev_height, FALSE); - - priv->fixed_height_check = 1; - } - - done: - if (validated_area) - { - GtkRequisition requisition; - int dummy; - - /* We temporarily guess a size, under the assumption that it will be the - * same when we get our next size_allocate. If we don't do this, we'll be - * in an inconsistent state when we call top_row_to_dy. */ - - /* FIXME: This is called from size_request, for some reason it is not infinitely - * recursing, we cannot call gtk_widget_get_preferred_size() here because that's - * not allowed (from inside ->get_preferred_width/height() implementations, one - * should call the vfuncs directly). However what is desired here is the full - * size including any margins and limited by any alignment (i.e. after - * GtkWidget:adjust_size_request() is called). - * - * Currently bypassing this but the real solution is to not update the scroll adjustments - * until we've received an allocation (never update scroll adjustments from size-requests). - */ - prevent_recursion_hack = TRUE; - gtk_tree_view_measure (GTK_WIDGET (tree_view), - GTK_ORIENTATION_HORIZONTAL, - -1, - &requisition.width, &dummy, - NULL, NULL); - gtk_tree_view_measure (GTK_WIDGET (tree_view), - GTK_ORIENTATION_VERTICAL, - -1, - &requisition.height, &dummy, - NULL, NULL); - prevent_recursion_hack = FALSE; - - /* If rows above the current position have changed height, this has - * affected the current view and thus needs a redraw. - */ - if (y != -1 && y < gtk_adjustment_get_value (priv->vadjustment)) - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - - gtk_adjustment_set_upper (priv->hadjustment, - MAX (gtk_adjustment_get_upper (priv->hadjustment), requisition.width)); - gtk_adjustment_set_upper (priv->vadjustment, - MAX (gtk_adjustment_get_upper (priv->vadjustment), requisition.height)); - - if (queue_resize) - gtk_widget_queue_resize (GTK_WIDGET (tree_view)); - } - - if (path) gtk_tree_path_free (path); - g_timer_destroy (timer); - - if (!retval && gtk_widget_get_mapped (GTK_WIDGET (tree_view))) - update_prelight (tree_view, - priv->event_last_x, - priv->event_last_y); - - return retval; -} - -static void -disable_adjustment_animation (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - gtk_adjustment_enable_animation (priv->vadjustment, - NULL, - gtk_adjustment_get_animation_duration (priv->vadjustment)); -} - -static void -maybe_reenable_adjustment_animation (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - if (priv->presize_handler_tick_cb != 0 || - priv->validate_rows_timer != 0) - return; - - gtk_adjustment_enable_animation (priv->vadjustment, - gtk_widget_get_frame_clock (GTK_WIDGET (tree_view)), - gtk_adjustment_get_animation_duration (priv->vadjustment)); -} - -static gboolean -do_presize_handler (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - if (priv->mark_rows_col_dirty) - { - if (priv->tree) - gtk_tree_rbtree_column_invalid (priv->tree); - priv->mark_rows_col_dirty = FALSE; - } - validate_visible_area (tree_view); - if (priv->presize_handler_tick_cb != 0) - { - gtk_widget_remove_tick_callback (GTK_WIDGET (tree_view), priv->presize_handler_tick_cb); - priv->presize_handler_tick_cb = 0; - } - - if (priv->fixed_height_mode) - { - GtkRequisition requisition; - - gtk_widget_get_preferred_size (GTK_WIDGET (tree_view), - &requisition, NULL); - - gtk_adjustment_set_upper (priv->hadjustment, - MAX (gtk_adjustment_get_upper (priv->hadjustment), requisition.width)); - gtk_adjustment_set_upper (priv->vadjustment, - MAX (gtk_adjustment_get_upper (priv->vadjustment), requisition.height)); - gtk_widget_queue_resize (GTK_WIDGET (tree_view)); - } - - maybe_reenable_adjustment_animation (tree_view); - - return FALSE; -} - -static gboolean -presize_handler_callback (GtkWidget *widget, - GdkFrameClock *clock, - gpointer unused) -{ - do_presize_handler (GTK_TREE_VIEW (widget)); - - return G_SOURCE_REMOVE; -} - -static gboolean -validate_rows (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - gboolean retval; - - if (priv->presize_handler_tick_cb) - { - do_presize_handler (tree_view); - return G_SOURCE_CONTINUE; - } - - retval = do_validate_rows (tree_view, TRUE); - - if (! retval && priv->validate_rows_timer) - { - g_source_remove (priv->validate_rows_timer); - priv->validate_rows_timer = 0; - maybe_reenable_adjustment_animation (tree_view); - } - - return retval; -} - -static void -install_presize_handler (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - if (! gtk_widget_get_realized (GTK_WIDGET (tree_view))) - return; - - disable_adjustment_animation (tree_view); - - if (! priv->presize_handler_tick_cb) - { - priv->presize_handler_tick_cb = - gtk_widget_add_tick_callback (GTK_WIDGET (tree_view), presize_handler_callback, NULL, NULL); - } - if (! priv->validate_rows_timer) - { - priv->validate_rows_timer = - g_idle_add_full (GTK_TREE_VIEW_PRIORITY_VALIDATE, (GSourceFunc) validate_rows, tree_view, NULL); - gdk_source_set_static_name_by_id (priv->validate_rows_timer, "[gtk] validate_rows"); - } -} - -static gboolean -scroll_sync_handler (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - if (gtk_tree_view_get_height (tree_view) <= gtk_adjustment_get_page_size (priv->vadjustment)) - gtk_adjustment_set_value (GTK_ADJUSTMENT (priv->vadjustment), 0); - else if (gtk_tree_row_reference_valid (priv->top_row)) - gtk_tree_view_top_row_to_dy (tree_view); - else - gtk_tree_view_dy_to_top_row (tree_view); - - priv->scroll_sync_timer = 0; - - return FALSE; -} - -static void -install_scroll_sync_handler (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - if (!gtk_widget_get_realized (GTK_WIDGET (tree_view))) - return; - - if (!priv->scroll_sync_timer) - { - priv->scroll_sync_timer = - g_idle_add_full (GTK_TREE_VIEW_PRIORITY_SCROLL_SYNC, (GSourceFunc) scroll_sync_handler, tree_view, NULL); - gdk_source_set_static_name_by_id (priv->scroll_sync_timer, "[gtk] scroll_sync_handler"); - } -} - -static void -gtk_tree_view_set_top_row (GtkTreeView *tree_view, - GtkTreePath *path, - int offset) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - gtk_tree_row_reference_free (priv->top_row); - - if (!path) - { - priv->top_row = NULL; - priv->top_row_dy = 0; - } - else - { - priv->top_row = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), priv->model, path); - priv->top_row_dy = offset; - } -} - -/* Always call this iff dy is in the visible range. If the tree is empty, then - * it’s set to be NULL, and top_row_dy is 0; - */ -static void -gtk_tree_view_dy_to_top_row (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - int offset; - GtkTreePath *path; - GtkTreeRBTree *tree; - GtkTreeRBNode *node; - - if (priv->tree == NULL) - { - gtk_tree_view_set_top_row (tree_view, NULL, 0); - } - else - { - offset = gtk_tree_rbtree_find_offset (priv->tree, - priv->dy, - &tree, &node); - - if (tree == NULL) - { - gtk_tree_view_set_top_row (tree_view, NULL, 0); - } - else - { - path = _gtk_tree_path_new_from_rbtree (tree, node); - gtk_tree_view_set_top_row (tree_view, path, offset); - gtk_tree_path_free (path); - } - } -} - -static void -gtk_tree_view_top_row_to_dy (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreePath *path; - GtkTreeRBTree *tree; - GtkTreeRBNode *node; - int new_dy; - - /* Avoid recursive calls */ - if (priv->in_top_row_to_dy) - return; - - if (gtk_adjustment_is_animating (priv->vadjustment)) - return; - - if (priv->top_row) - path = gtk_tree_row_reference_get_path (priv->top_row); - else - path = NULL; - - if (!path) - tree = NULL; - else - _gtk_tree_view_find_node (tree_view, path, &tree, &node); - - if (path) - gtk_tree_path_free (path); - - if (tree == NULL) - { - /* keep dy and set new toprow */ - gtk_tree_row_reference_free (priv->top_row); - priv->top_row = NULL; - priv->top_row_dy = 0; - /* DO NOT install the idle handler */ - gtk_tree_view_dy_to_top_row (tree_view); - return; - } - - if (gtk_tree_view_get_row_height (tree_view, node) - < priv->top_row_dy) - { - /* new top row -- do NOT install the idle handler */ - gtk_tree_view_dy_to_top_row (tree_view); - return; - } - - new_dy = gtk_tree_rbtree_node_find_offset (tree, node); - new_dy += priv->top_row_dy; - - if (new_dy + gtk_adjustment_get_page_size (priv->vadjustment) > gtk_tree_view_get_height (tree_view)) - new_dy = gtk_tree_view_get_height (tree_view) - gtk_adjustment_get_page_size (priv->vadjustment); - - new_dy = MAX (0, new_dy); - - priv->in_top_row_to_dy = TRUE; - gtk_adjustment_set_value (priv->vadjustment, (double)new_dy); - priv->in_top_row_to_dy = FALSE; -} - - -void -_gtk_tree_view_install_mark_rows_col_dirty (GtkTreeView *tree_view, - gboolean install_handler) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - priv->mark_rows_col_dirty = TRUE; - - if (install_handler) - install_presize_handler (tree_view); -} - -/* - * This function works synchronously (due to the while (validate_rows...) - * loop). - * - * There was a check for column_type != GTK_TREE_VIEW_COLUMN_AUTOSIZE - * here. You now need to check that yourself. - */ -void -_gtk_tree_view_column_autosize (GtkTreeView *tree_view, - GtkTreeViewColumn *column) -{ - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (column)); - - _gtk_tree_view_column_cell_set_dirty (column, FALSE); - - do_presize_handler (tree_view); - while (validate_rows (tree_view)); - - gtk_widget_queue_resize (GTK_WIDGET (tree_view)); -} - -/* Drag-and-drop */ - -typedef struct -{ - GtkTreeRowReference *dest_row; - guint path_down_mode : 1; - guint empty_view_drop : 1; - guint drop_append_mode : 1; -} -DestRow; - -static void -dest_row_free (gpointer data) -{ - DestRow *dr = (DestRow *)data; - - gtk_tree_row_reference_free (dr->dest_row); - g_slice_free (DestRow, dr); -} - -static void -set_dest_row (GdkDrop *drop, - GtkTreeModel *model, - GtkTreePath *dest_row, - gboolean path_down_mode, - gboolean empty_view_drop, - gboolean drop_append_mode) -{ - DestRow *dr; - - if (!dest_row) - { - g_object_set_data_full (G_OBJECT (drop), I_("gtk-tree-view-dest-row"), - NULL, NULL); - return; - } - - dr = g_slice_new (DestRow); - - dr->dest_row = gtk_tree_row_reference_new (model, dest_row); - dr->path_down_mode = path_down_mode != FALSE; - dr->empty_view_drop = empty_view_drop != FALSE; - dr->drop_append_mode = drop_append_mode != FALSE; - - g_object_set_data_full (G_OBJECT (drop), I_("gtk-tree-view-dest-row"), - dr, (GDestroyNotify) dest_row_free); -} - -static GtkTreePath* -get_dest_row (GdkDrop *drop, - gboolean *path_down_mode) -{ - DestRow *dr = - g_object_get_data (G_OBJECT (drop), "gtk-tree-view-dest-row"); - - if (dr) - { - GtkTreePath *path = NULL; - - if (path_down_mode) - *path_down_mode = dr->path_down_mode; - - if (dr->dest_row) - path = gtk_tree_row_reference_get_path (dr->dest_row); - else if (dr->empty_view_drop) - path = gtk_tree_path_new_from_indices (0, -1); - else - path = NULL; - - if (path && dr->drop_append_mode) - gtk_tree_path_next (path); - - return path; - } - else - return NULL; -} - -/* Get/set whether drag_motion requested the drag data and - * drag_data_received should thus not actually insert the data, - * since the data doesn’t result from a drop. - */ -static void -set_status_pending (GdkDrop *drop, - GdkDragAction suggested_action) -{ - g_object_set_data (G_OBJECT (drop), - I_("gtk-tree-view-status-pending"), - GINT_TO_POINTER (suggested_action)); -} - -static GdkDragAction -get_status_pending (GdkDrop *drop) -{ - return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (drop), - "gtk-tree-view-status-pending")); -} - -static TreeViewDragInfo* -get_info (GtkTreeView *tree_view) -{ - return g_object_get_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info"); -} - -static void -destroy_info (TreeViewDragInfo *di) -{ - g_clear_pointer (&di->source_formats, gdk_content_formats_unref); - g_clear_pointer (&di->source_item, gtk_tree_row_reference_free); - g_clear_object (&di->dest); - - g_slice_free (TreeViewDragInfo, di); -} - -static TreeViewDragInfo* -ensure_info (GtkTreeView *tree_view) -{ - TreeViewDragInfo *di; - - di = get_info (tree_view); - - if (di == NULL) - { - di = g_slice_new0 (TreeViewDragInfo); - - g_object_set_data_full (G_OBJECT (tree_view), - I_("gtk-tree-view-drag-info"), - di, - (GDestroyNotify) destroy_info); - } - - return di; -} - -static void -remove_info (GtkTreeView *tree_view) -{ - TreeViewDragInfo *di; - - di = get_info (tree_view); - if (di && di->dest) - gtk_widget_remove_controller (GTK_WIDGET (tree_view), GTK_EVENT_CONTROLLER (di->dest)); - g_object_set_data (G_OBJECT (tree_view), I_("gtk-tree-view-drag-info"), NULL); -} - -static void -add_scroll_timeout (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - if (priv->scroll_timeout == 0) - { - priv->scroll_timeout = g_timeout_add (150, scroll_row_timeout, tree_view); - gdk_source_set_static_name_by_id (priv->scroll_timeout, "[gtk] scroll_row_timeout"); - } -} - -static void -remove_scroll_timeout (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_clear_handle_id (&priv->scroll_timeout, g_source_remove); -} - -static gboolean -check_model_dnd (GtkTreeModel *model, - GType required_iface, - const char *signal) -{ - if (model == NULL || !G_TYPE_CHECK_INSTANCE_TYPE ((model), required_iface)) - { - g_warning ("You must override the default '%s' handler " - "on GtkTreeView when using models that don't support " - "the %s interface and enabling drag-and-drop. The simplest way to do this " - "is to connect to '%s' and call " - "g_signal_stop_emission_by_name() in your signal handler to prevent " - "the default handler from running. Look at the source code " - "for the default handler in gtktreeview.c to get an idea what " - "your handler should do. (gtktreeview.c is in the GTK source " - "code.) If you're using GTK from a language other than C, " - "there may be a more natural way to override default handlers, e.g. via derivation.", - signal, g_type_name (required_iface), signal); - return FALSE; - } - else - return TRUE; -} - -static void -remove_open_timeout (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_clear_handle_id (&priv->open_dest_timeout, g_source_remove); -} - - -static int -open_row_timeout (gpointer data) -{ - GtkTreeView *tree_view = data; - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreePath *dest_path = NULL; - GtkTreeViewDropPosition pos; - gboolean result = FALSE; - - gtk_tree_view_get_drag_dest_row (tree_view, - &dest_path, - &pos); - - if (dest_path && - (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER || - pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) - { - gtk_tree_view_expand_row (tree_view, dest_path, FALSE); - priv->open_dest_timeout = 0; - - gtk_tree_path_free (dest_path); - } - else - { - if (dest_path) - gtk_tree_path_free (dest_path); - - result = TRUE; - } - - return result; -} - -static gboolean -scroll_row_timeout (gpointer data) -{ - GtkTreeView *tree_view = data; - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - gtk_tree_view_vertical_autoscroll (tree_view); - - if (priv->rubber_band_status == RUBBER_BAND_ACTIVE) - gtk_tree_view_update_rubber_band (tree_view); - - return TRUE; -} - -static GdkDragAction -gtk_tree_view_get_action (GtkWidget *widget, - GdkDrop *drop) -{ - GtkTreeView *tree_view = GTK_TREE_VIEW (widget); - TreeViewDragInfo *di; - GdkDrag *drag = gdk_drop_get_drag (drop); - GdkDragAction actions; - - di = get_info (tree_view); - - actions = gdk_drop_get_actions (drop); - - if (di && di->drag == drag && - actions & GDK_ACTION_MOVE) - return GDK_ACTION_MOVE; - - if (actions & GDK_ACTION_COPY) - return GDK_ACTION_COPY; - - if (actions & GDK_ACTION_MOVE) - return GDK_ACTION_MOVE; - - return 0; -} - -/* Returns TRUE if event should not be propagated to parent widgets */ -static gboolean -set_destination_row (GtkTreeView *tree_view, - GdkDrop *drop, - GtkDropTargetAsync *dest, - /* coordinates relative to the widget */ - int x, - int y, - GdkDragAction *suggested_action, - GType *target) -{ - GtkTreePath *path = NULL; - GtkTreeViewDropPosition pos; - GtkTreeViewDropPosition old_pos; - TreeViewDragInfo *di; - GtkWidget *widget; - GtkTreePath *old_dest_path = NULL; - gboolean can_drop = FALSE; - GdkContentFormats *formats; - - *suggested_action = 0; - *target = G_TYPE_INVALID; - - widget = GTK_WIDGET (tree_view); - - di = get_info (tree_view); - - if (di == NULL || y - gtk_tree_view_get_effective_header_height (tree_view) < 0) - { - /* someone unset us as a drag dest, note that if - * we return FALSE drag_leave isn't called - */ - - gtk_tree_view_set_drag_dest_row (tree_view, - NULL, - GTK_TREE_VIEW_DROP_BEFORE); - - remove_scroll_timeout (GTK_TREE_VIEW (widget)); - remove_open_timeout (GTK_TREE_VIEW (widget)); - - return FALSE; /* no longer a drop site */ - } - - formats = gtk_drop_target_async_get_formats (dest); - *target = gdk_content_formats_match_gtype (formats, formats); - if (*target == G_TYPE_INVALID) - return FALSE; - - if (!gtk_tree_view_get_dest_row_at_pos (tree_view, - x, y, - &path, - &pos)) - { - int n_children; - GtkTreeModel *model; - - remove_open_timeout (tree_view); - - /* the row got dropped on empty space, let's setup a special case - */ - - if (path) - gtk_tree_path_free (path); - - model = gtk_tree_view_get_model (tree_view); - - n_children = gtk_tree_model_iter_n_children (model, NULL); - if (n_children) - { - pos = GTK_TREE_VIEW_DROP_AFTER; - path = gtk_tree_path_new_from_indices (n_children - 1, -1); - } - else - { - pos = GTK_TREE_VIEW_DROP_BEFORE; - path = gtk_tree_path_new_from_indices (0, -1); - } - - can_drop = TRUE; - - goto out; - } - - g_assert (path); - - /* If we left the current row's "open" zone, unset the timeout for - * opening the row - */ - gtk_tree_view_get_drag_dest_row (tree_view, - &old_dest_path, - &old_pos); - - if (old_dest_path && - (gtk_tree_path_compare (path, old_dest_path) != 0 || - !(pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER || - pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE))) - remove_open_timeout (tree_view); - - if (old_dest_path) - gtk_tree_path_free (old_dest_path); - - if (TRUE /* FIXME if the location droppable predicate */) - { - can_drop = TRUE; - } - -out: - if (can_drop) - { - *suggested_action = gtk_tree_view_get_action (widget, drop); - - gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), - path, pos); - } - else - { - /* can't drop here */ - remove_open_timeout (tree_view); - - gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), - NULL, - GTK_TREE_VIEW_DROP_BEFORE); - } - - if (path) - gtk_tree_path_free (path); - - return TRUE; -} - -static GtkTreePath* -get_logical_dest_row (GtkTreeView *tree_view, - gboolean *path_down_mode, - gboolean *drop_append_mode) -{ - /* adjust path to point to the row the drop goes in front of */ - GtkTreePath *path = NULL; - GtkTreeViewDropPosition pos; - - g_return_val_if_fail (path_down_mode != NULL, NULL); - g_return_val_if_fail (drop_append_mode != NULL, NULL); - - *path_down_mode = FALSE; - *drop_append_mode = 0; - - gtk_tree_view_get_drag_dest_row (tree_view, &path, &pos); - - if (path == NULL) - return NULL; - - if (pos == GTK_TREE_VIEW_DROP_BEFORE) - ; /* do nothing */ - else if (pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE || - pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER) - *path_down_mode = TRUE; - else - { - GtkTreeIter iter; - GtkTreeModel *model = gtk_tree_view_get_model (tree_view); - - g_assert (pos == GTK_TREE_VIEW_DROP_AFTER); - - if (!gtk_tree_model_get_iter (model, &iter, path) || - !gtk_tree_model_iter_next (model, &iter)) - *drop_append_mode = 1; - else - { - *drop_append_mode = 0; - gtk_tree_path_next (path); - } - } - - return path; -} - -static gboolean -gtk_tree_view_maybe_begin_dragging_row (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkWidget *widget = GTK_WIDGET (tree_view); - double start_x, start_y, offset_x, offset_y; - TreeViewDragInfo *di; - GtkTreePath *path = NULL; - int button; - GtkTreeModel *model; - gboolean retval = FALSE; - int bin_x, bin_y; - GdkSurface *surface; - GdkDevice *device; - GdkContentProvider *content; - GdkDrag *drag; - GdkPaintable *icon; - - di = get_info (tree_view); - - if (di == NULL || !di->source_set) - goto out; - - if (!gtk_gesture_is_recognized (priv->drag_gesture)) - goto out; - - gtk_gesture_drag_get_start_point (GTK_GESTURE_DRAG (priv->drag_gesture), - &start_x, &start_y); - gtk_gesture_drag_get_offset (GTK_GESTURE_DRAG (priv->drag_gesture), - &offset_x, &offset_y); - - if (!gtk_drag_check_threshold_double (widget, 0, 0, offset_x, offset_y)) - goto out; - - model = gtk_tree_view_get_model (tree_view); - - if (model == NULL) - goto out; - - button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (priv->drag_gesture)); - - /* Deny the click gesture */ - gtk_gesture_set_state (GTK_GESTURE (priv->click_gesture), - GTK_EVENT_SEQUENCE_DENIED); - - gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, start_x, start_y, - &bin_x, &bin_y); - gtk_tree_view_get_path_at_pos (tree_view, bin_x, bin_y, &path, - NULL, NULL, NULL); - - if (path == NULL) - goto out; - - if (!GTK_IS_TREE_DRAG_SOURCE (model) || - !gtk_tree_drag_source_row_draggable (GTK_TREE_DRAG_SOURCE (model), - path)) - goto out; - - if (!(GDK_BUTTON1_MASK << (button - 1) & di->start_button_mask)) - goto out; - - /* Now we can begin the drag */ - gtk_gesture_set_state (GTK_GESTURE (priv->drag_gesture), - GTK_EVENT_SEQUENCE_CLAIMED); - - surface = gtk_native_get_surface (gtk_widget_get_native (GTK_WIDGET (tree_view))); - device = gtk_gesture_get_device (GTK_GESTURE (priv->drag_gesture)), - content = gtk_tree_view_drag_data_get (tree_view, path); - if (content == NULL) - goto out; - - retval = TRUE; - - drag = gdk_drag_begin (surface, device, content, di->source_actions, start_x, start_y); - - g_object_unref (content); - - g_signal_connect (drag, "dnd-finished", G_CALLBACK (gtk_tree_view_dnd_finished_cb), tree_view); - - icon = gtk_tree_view_create_row_drag_icon (tree_view, path); - gtk_drag_icon_set_from_paintable (drag, icon, priv->press_start_x + 1, 1); - g_object_unref (icon); - - di->drag = drag; - - g_object_unref (drag); - - di->source_item = gtk_tree_row_reference_new (model, path); - - out: - if (path) - gtk_tree_path_free (path); - - return retval; -} - -static void -gtk_tree_view_dnd_finished_cb (GdkDrag *drag, - GtkWidget *widget) -{ - GtkTreeView *tree_view = GTK_TREE_VIEW (widget); - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - TreeViewDragInfo *di; - GtkTreeModel *model; - GtkTreePath *source_row; - - priv->event_last_x = -10000; - priv->event_last_y = -10000; - - if (gdk_drag_get_selected_action (drag) != GDK_ACTION_MOVE) - return; - - tree_view = GTK_TREE_VIEW (widget); - model = gtk_tree_view_get_model (tree_view); - - if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_SOURCE, "drag_data_delete")) - return; - - di = get_info (tree_view); - - if (di == NULL || di->source_item == NULL) - return; - - source_row = gtk_tree_row_reference_get_path (di->source_item); - - if (source_row == NULL) - return; - - gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (model), source_row); - - gtk_tree_path_free (source_row); - - g_clear_pointer (&di->source_item, gtk_tree_row_reference_free); -} - -/* Default signal implementations for the drag signals */ -static GdkContentProvider * -gtk_tree_view_drag_data_get (GtkTreeView *tree_view, - GtkTreePath *source_row) -{ - GtkTreeModel *model; - GdkContentProvider *content; - - model = gtk_tree_view_get_model (tree_view); - - if (model == NULL) - return NULL; - - /* We can implement the GTK_TREE_MODEL_ROW target generically for - * any model; for DragSource models there are some other targets - * we also support. - */ - - if (GTK_IS_TREE_DRAG_SOURCE (model)) - content = gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (model), source_row); - else - content = NULL; - - /* If drag_data_get does nothing, try providing row data. */ - if (!content) - content = gtk_tree_create_row_drag_content (model, source_row); - - return content; -} - -static void -gtk_tree_view_drag_leave (GtkDropTargetAsync *dest, - GdkDrop *drop, - GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - /* unset any highlight row */ - gtk_tree_view_set_drag_dest_row (tree_view, - NULL, - GTK_TREE_VIEW_DROP_BEFORE); - - remove_scroll_timeout (tree_view); - remove_open_timeout (tree_view); - - priv->event_last_x = -10000; - priv->event_last_y = -10000; -} - - -static GdkDragAction -gtk_tree_view_drag_motion (GtkDropTargetAsync *dest, - GdkDrop *drop, - double x, - double y, - GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - gboolean empty; - GtkTreePath *path = NULL; - GtkTreeViewDropPosition pos; - GdkDragAction suggested_action = 0; - GType target; - - if (!set_destination_row (tree_view, drop, dest, x, y, &suggested_action, &target)) - return 0; - - priv->event_last_x = x; - priv->event_last_y = y; - - gtk_tree_view_get_drag_dest_row (tree_view, &path, &pos); - - /* we only know this *after* set_desination_row */ - empty = priv->empty_view_drop; - - if (path == NULL && !empty) - { - suggested_action = 0; - } - else - { - if (priv->open_dest_timeout == 0 && - (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER || - pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) - { - priv->open_dest_timeout = - g_timeout_add (AUTO_EXPAND_TIMEOUT, open_row_timeout, tree_view); - gdk_source_set_static_name_by_id (priv->open_dest_timeout, "[gtk] open_row_timeout"); - } - else - { - add_scroll_timeout (tree_view); - } - - if (target == GTK_TYPE_TREE_ROW_DATA) - { - /* Request data so we can use the source row when - * determining whether to accept the drop - */ - set_status_pending (drop, suggested_action); - gdk_drop_read_value_async (drop, GTK_TYPE_TREE_ROW_DATA, G_PRIORITY_DEFAULT, NULL, gtk_tree_view_drag_data_received, tree_view); - } - else - { - set_status_pending (drop, 0); - } - } - - if (path) - gtk_tree_path_free (path); - - return suggested_action; -} - - -static gboolean -gtk_tree_view_drag_drop (GtkDropTargetAsync *dest, - GdkDrop *drop, - double x, - double y, - GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreePath *path; - GdkDragAction suggested_action = 0; - GType target = G_TYPE_INVALID; - TreeViewDragInfo *di; - GtkTreeModel *model; - gboolean path_down_mode; - gboolean drop_append_mode; - - model = gtk_tree_view_get_model (tree_view); - - remove_scroll_timeout (tree_view); - remove_open_timeout (tree_view); - - di = get_info (tree_view); - - if (di == NULL) - return FALSE; - - if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_drop")) - return FALSE; - - if (!set_destination_row (tree_view, drop, dest, x, y, &suggested_action, &target)) - return FALSE; - - path = get_logical_dest_row (tree_view, &path_down_mode, &drop_append_mode); - - if (target != G_TYPE_INVALID && path != NULL) - { - /* in case a motion had requested drag data, change things so we - * treat drag data receives as a drop. - */ - set_status_pending (drop, 0); - set_dest_row (drop, model, path, - path_down_mode, priv->empty_view_drop, - drop_append_mode); - } - - if (path) - gtk_tree_path_free (path); - - /* Unset this thing */ - gtk_tree_view_set_drag_dest_row (tree_view, - NULL, - GTK_TREE_VIEW_DROP_BEFORE); - - if (target != G_TYPE_INVALID) - { - gdk_drop_read_value_async (drop, GTK_TYPE_TREE_ROW_DATA, G_PRIORITY_DEFAULT, NULL, gtk_tree_view_drag_data_received, tree_view); - return TRUE; - } - else - return FALSE; -} - -static void -gtk_tree_view_drag_data_received (GObject *source, - GAsyncResult *result, - gpointer data) -{ - GtkTreeView *tree_view = GTK_TREE_VIEW (data); - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GdkDrop *drop = GDK_DROP (source); - GtkTreePath *path; - TreeViewDragInfo *di; - GtkTreeModel *model; - GtkTreePath *dest_row; - GdkDragAction suggested_action; - gboolean path_down_mode; - gboolean drop_append_mode; - const GValue *value; - - value = gdk_drop_read_value_finish (drop, result, NULL); - if (value == NULL) - return; - - model = gtk_tree_view_get_model (tree_view); - - if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_data_received")) - return; - - di = get_info (tree_view); - - if (di == NULL) - return; - - suggested_action = get_status_pending (drop); - - if (suggested_action) - { - /* We are getting this data due to a request in drag_motion, - * rather than due to a request in drag_drop, so we are just - * supposed to call drag_status, not actually paste in the - * data. - */ - path = get_logical_dest_row (tree_view, &path_down_mode, - &drop_append_mode); - - if (path == NULL) - suggested_action = 0; - else if (path_down_mode) - gtk_tree_path_down (path); - - if (suggested_action) - { - if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model), - path, - value)) - { - if (path_down_mode) - { - path_down_mode = FALSE; - gtk_tree_path_up (path); - - if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model), - path, - value)) - suggested_action = 0; - } - else - suggested_action = 0; - } - } - - if (path) - gtk_tree_path_free (path); - - /* If you can't drop, remove user drop indicator until the next motion */ - if (suggested_action == 0) - gtk_tree_view_set_drag_dest_row (tree_view, - NULL, - GTK_TREE_VIEW_DROP_BEFORE); - - return; - } - - dest_row = get_dest_row (drop, &path_down_mode); - - if (dest_row == NULL) - return; - - if (path_down_mode) - { - gtk_tree_path_down (dest_row); - if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model), - dest_row, value)) - gtk_tree_path_up (dest_row); - } - - suggested_action = gtk_tree_view_get_action (GTK_WIDGET (tree_view), drop); - - if (suggested_action && - !gtk_tree_drag_dest_drag_data_received (GTK_TREE_DRAG_DEST (model), - dest_row, - value)) - suggested_action = 0; - - gdk_drop_finish (drop, suggested_action); - - if (gtk_tree_path_get_depth (dest_row) == 1 && - gtk_tree_path_get_indices (dest_row)[0] == 0 && - gtk_tree_model_iter_n_children (priv->model, NULL) != 0) - { - /* special case drag to "0", scroll to first item */ - if (!priv->scroll_to_path) - gtk_tree_view_scroll_to_cell (tree_view, dest_row, NULL, FALSE, 0.0, 0.0); - } - - gtk_tree_path_free (dest_row); - - /* drop dest_row */ - set_dest_row (drop, NULL, NULL, FALSE, FALSE, FALSE); -} - -static void -gtk_tree_view_remove (GtkTreeView *tree_view, - GtkWidget *widget) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreeViewChild *child = NULL; - GList *tmp_list; - - tmp_list = priv->children; - while (tmp_list) - { - child = tmp_list->data; - if (child->widget == widget) - { - gtk_widget_unparent (widget); - - priv->children = g_list_remove_link (priv->children, tmp_list); - g_list_free_1 (tmp_list); - g_slice_free (GtkTreeViewChild, child); - return; - } - - tmp_list = tmp_list->next; - } - - tmp_list = priv->columns; - - while (tmp_list) - { - GtkTreeViewColumn *column; - GtkWidget *button; - - column = tmp_list->data; - button = gtk_tree_view_column_get_button (column); - - if (button == widget) - { - gtk_widget_unparent (widget); - return; - } - tmp_list = tmp_list->next; - } -} - -/* Returns TRUE is any of the columns contains a cell that can-focus. - * If this is not the case, a column-spanning focus rectangle will be - * drawn. - */ -static gboolean -gtk_tree_view_has_can_focus_cell (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GList *list; - - for (list = priv->columns; list; list = list->next) - { - GtkTreeViewColumn *column = list->data; - - if (!gtk_tree_view_column_get_visible (column)) - continue; - if (gtk_cell_area_is_activatable (gtk_cell_layout_get_area (GTK_CELL_LAYOUT (column)))) - return TRUE; - } - - return FALSE; -} - -static void -column_sizing_notify (GObject *object, - GParamSpec *pspec, - gpointer data) -{ - GtkTreeViewColumn *c = GTK_TREE_VIEW_COLUMN (object); - - if (gtk_tree_view_column_get_sizing (c) != GTK_TREE_VIEW_COLUMN_FIXED) - /* disable fixed height mode */ - g_object_set (data, "fixed-height-mode", FALSE, NULL); -} - -/** - * gtk_tree_view_set_fixed_height_mode: - * @tree_view: a `GtkTreeView` - * @enable: %TRUE to enable fixed height mode - * - * Enables or disables the fixed height mode of @tree_view. - * Fixed height mode speeds up `GtkTreeView` by assuming that all - * rows have the same height. - * Only enable this option if all rows are the same height and all - * columns are of type %GTK_TREE_VIEW_COLUMN_FIXED. - **/ -void -gtk_tree_view_set_fixed_height_mode (GtkTreeView *tree_view, - gboolean enable) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GList *l; - - enable = enable != FALSE; - - if (enable == priv->fixed_height_mode) - return; - - if (!enable) - { - priv->fixed_height_mode = 0; - priv->fixed_height = -1; - } - else - { - /* make sure all columns are of type FIXED */ - for (l = priv->columns; l; l = l->next) - { - g_return_if_fail (gtk_tree_view_column_get_sizing (l->data) == GTK_TREE_VIEW_COLUMN_FIXED); - } - - /* yes, we really have to do this is in a separate loop */ - for (l = priv->columns; l; l = l->next) - g_signal_connect (l->data, "notify::sizing", - G_CALLBACK (column_sizing_notify), tree_view); - - priv->fixed_height_mode = 1; - priv->fixed_height = -1; - } - - /* force a revalidation */ - install_presize_handler (tree_view); - - g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_FIXED_HEIGHT_MODE]); -} - -/** - * gtk_tree_view_get_fixed_height_mode: - * @tree_view: a `GtkTreeView` - * - * Returns whether fixed height mode is turned on for @tree_view. - * - * Returns: %TRUE if @tree_view is in fixed height mode - **/ -gboolean -gtk_tree_view_get_fixed_height_mode (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - return priv->fixed_height_mode; -} - -/* Returns TRUE if the focus is within the headers, after the focus operation is - * done - */ -static gboolean -gtk_tree_view_header_focus (GtkTreeView *tree_view, - GtkDirectionType dir, - gboolean clamp_column_visible) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreeViewColumn *column; - GtkWidget *button; - GtkWidget *focus_child; - GList *last_column, *first_column; - GList *tmp_list; - gboolean rtl; - - if (! priv->headers_visible) - return FALSE; - - focus_child = gtk_widget_get_focus_child (GTK_WIDGET (tree_view)); - - first_column = priv->columns; - while (first_column) - { - column = GTK_TREE_VIEW_COLUMN (first_column->data); - button = gtk_tree_view_column_get_button (column); - - if (gtk_widget_get_focusable (button) && - gtk_tree_view_column_get_visible (column) && - (gtk_tree_view_column_get_clickable (column) || - gtk_tree_view_column_get_reorderable (column))) - break; - first_column = first_column->next; - } - - /* No headers are visible, or are focusable. We can't focus in or out. - */ - if (first_column == NULL) - return FALSE; - - last_column = g_list_last (priv->columns); - while (last_column) - { - column = GTK_TREE_VIEW_COLUMN (last_column->data); - button = gtk_tree_view_column_get_button (column); - - if (gtk_widget_get_focusable (button) && - gtk_tree_view_column_get_visible (column) && - (gtk_tree_view_column_get_clickable (column) || - gtk_tree_view_column_get_reorderable (column))) - break; - last_column = last_column->prev; - } - - - rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); - - switch (dir) - { - case GTK_DIR_TAB_BACKWARD: - case GTK_DIR_TAB_FORWARD: - case GTK_DIR_UP: - case GTK_DIR_DOWN: - if (focus_child == NULL) - { - if (priv->focus_column != NULL) - button = gtk_tree_view_column_get_button (priv->focus_column); - else - button = NULL; - - if (button && gtk_widget_get_focusable (button)) - focus_child = button; - else - focus_child = gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (first_column->data)); - - gtk_widget_grab_focus (focus_child); - break; - } - return FALSE; - - case GTK_DIR_LEFT: - case GTK_DIR_RIGHT: - if (focus_child == NULL) - { - if (priv->focus_column != NULL) - focus_child = gtk_tree_view_column_get_button (priv->focus_column); - else if (dir == GTK_DIR_LEFT) - focus_child = gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (last_column->data)); - else - focus_child = gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (first_column->data)); - - gtk_widget_grab_focus (focus_child); - break; - } - - if (gtk_widget_child_focus (focus_child, dir)) - { - /* The focus moves inside the button. */ - /* This is probably a great example of bad UI */ - break; - } - - /* We need to move the focus among the row of buttons. */ - for (tmp_list = priv->columns; tmp_list; tmp_list = tmp_list->next) - if (gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (tmp_list->data)) == focus_child) - break; - - if ((tmp_list == first_column && dir == (rtl ? GTK_DIR_RIGHT : GTK_DIR_LEFT)) - || (tmp_list == last_column && dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT))) - { - gtk_widget_error_bell (GTK_WIDGET (tree_view)); - break; - } - - while (tmp_list) - { - if (dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT)) - tmp_list = tmp_list->next; - else - tmp_list = tmp_list->prev; - - if (tmp_list == NULL) - { - g_warning ("Internal button not found"); - break; - } - column = tmp_list->data; - button = gtk_tree_view_column_get_button (column); - if (button && - gtk_tree_view_column_get_visible (column) && - gtk_widget_get_focusable (button)) - { - focus_child = button; - gtk_widget_grab_focus (button); - break; - } - } - break; - default: - g_assert_not_reached (); - break; - } - - /* if focus child is non-null, we assume it's been set to the current focus child - */ - if (focus_child) - { - for (tmp_list = priv->columns; tmp_list; tmp_list = tmp_list->next) - if (gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (tmp_list->data)) == focus_child) - { - _gtk_tree_view_set_focus_column (tree_view, GTK_TREE_VIEW_COLUMN (tmp_list->data)); - break; - } - - if (clamp_column_visible) - { - gtk_tree_view_clamp_column_visible (tree_view, - priv->focus_column, - FALSE); - } - } - - return (focus_child != NULL); -} - -/* This function returns in 'path' the first focusable path, if the given path - * is already focusable, it’s the returned one. - */ -static gboolean -search_first_focusable_path (GtkTreeView *tree_view, - GtkTreePath **path, - gboolean search_forward, - GtkTreeRBTree **new_tree, - GtkTreeRBNode **new_node) -{ - GtkTreeRBTree *tree = NULL; - GtkTreeRBNode *node = NULL; - - if (!path || !*path) - return FALSE; - - _gtk_tree_view_find_node (tree_view, *path, &tree, &node); - - if (!tree || !node) - return FALSE; - - while (node && row_is_separator (tree_view, NULL, *path)) - { - if (search_forward) - gtk_tree_rbtree_next_full (tree, node, &tree, &node); - else - gtk_tree_rbtree_prev_full (tree, node, &tree, &node); - - if (*path) - gtk_tree_path_free (*path); - - if (node) - *path = _gtk_tree_path_new_from_rbtree (tree, node); - else - *path = NULL; - } - - if (new_tree) - *new_tree = tree; - - if (new_node) - *new_node = node; - - return (*path != NULL); -} - -static int -gtk_tree_view_focus (GtkWidget *widget, - GtkDirectionType direction) -{ - GtkTreeView *tree_view = GTK_TREE_VIEW (widget); - GtkWidget *focus_child; - - focus_child = gtk_widget_get_focus_child (widget); - - gtk_tree_view_stop_editing (GTK_TREE_VIEW (widget), FALSE); - /* Case 1. Headers currently have focus. */ - if (focus_child) - { - switch (direction) - { - case GTK_DIR_LEFT: - case GTK_DIR_RIGHT: - gtk_tree_view_header_focus (tree_view, direction, TRUE); - return TRUE; - case GTK_DIR_TAB_BACKWARD: - case GTK_DIR_UP: - return FALSE; - case GTK_DIR_TAB_FORWARD: - case GTK_DIR_DOWN: - return gtk_widget_grab_focus (widget); - default: - g_assert_not_reached (); - return FALSE; - } - } - - /* Case 2. We don't have focus at all. */ - if (!gtk_widget_has_focus (widget)) - { - return gtk_widget_grab_focus (widget); - } - - /* Case 3. We have focus already. */ - if (direction == GTK_DIR_TAB_BACKWARD) - return (gtk_tree_view_header_focus (tree_view, direction, FALSE)); - else if (direction == GTK_DIR_TAB_FORWARD) - return FALSE; - - /* Other directions caught by the keybindings */ - return gtk_widget_grab_focus (widget); -} - -static gboolean -gtk_tree_view_grab_focus (GtkWidget *widget) -{ - if (!gtk_widget_grab_focus_self (widget)) - return FALSE; - - gtk_tree_view_focus_to_cursor (GTK_TREE_VIEW (widget)); - return TRUE; -} - -static void -gtk_tree_view_css_changed (GtkWidget *widget, - GtkCssStyleChange *change) -{ - GtkTreeView *tree_view = GTK_TREE_VIEW (widget); - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GList *list; - GtkTreeViewColumn *column; - - GTK_WIDGET_CLASS (gtk_tree_view_parent_class)->css_changed (widget, change); - - if (gtk_widget_get_realized (widget)) - { - gtk_tree_view_set_grid_lines (tree_view, priv->grid_lines); - gtk_tree_view_set_enable_tree_lines (tree_view, priv->tree_lines_enabled); - } - - if (change == NULL || gtk_css_style_change_affects (change, GTK_CSS_AFFECTS_SIZE)) - { - for (list = priv->columns; list; list = list->next) - { - column = list->data; - _gtk_tree_view_column_cell_set_dirty (column, TRUE); - } - - priv->fixed_height = -1; - gtk_tree_rbtree_mark_invalid (priv->tree); - } - - /* Invalidate expander size */ - priv->expander_size = -1; -} - -static gboolean -gtk_tree_view_real_move_cursor (GtkTreeView *tree_view, - GtkMovementStep step, - int count, - gboolean extend, - gboolean modify) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); - g_return_val_if_fail (step == GTK_MOVEMENT_LOGICAL_POSITIONS || - step == GTK_MOVEMENT_VISUAL_POSITIONS || - step == GTK_MOVEMENT_DISPLAY_LINES || - step == GTK_MOVEMENT_PAGES || - step == GTK_MOVEMENT_BUFFER_ENDS, FALSE); - - if (priv->tree == NULL) - return FALSE; - if (!gtk_widget_has_focus (GTK_WIDGET (tree_view))) - return FALSE; - - gtk_tree_view_stop_editing (tree_view, FALSE); - priv->draw_keyfocus = TRUE; - gtk_widget_grab_focus (GTK_WIDGET (tree_view)); - - priv->modify_selection_pressed = modify; - priv->extend_selection_pressed = extend; - - switch (step) - { - /* currently we make no distinction. When we go bi-di, we need to */ - case GTK_MOVEMENT_LOGICAL_POSITIONS: - case GTK_MOVEMENT_VISUAL_POSITIONS: - gtk_tree_view_move_cursor_left_right (tree_view, count); - break; - case GTK_MOVEMENT_DISPLAY_LINES: - gtk_tree_view_move_cursor_up_down (tree_view, count); - break; - case GTK_MOVEMENT_PAGES: - gtk_tree_view_move_cursor_page_up_down (tree_view, count); - break; - case GTK_MOVEMENT_BUFFER_ENDS: - gtk_tree_view_move_cursor_start_end (tree_view, count); - break; - case GTK_MOVEMENT_WORDS: - case GTK_MOVEMENT_DISPLAY_LINE_ENDS: - case GTK_MOVEMENT_PARAGRAPHS: - case GTK_MOVEMENT_PARAGRAPH_ENDS: - case GTK_MOVEMENT_HORIZONTAL_PAGES: - default: - g_assert_not_reached (); - } - - priv->modify_selection_pressed = FALSE; - priv->extend_selection_pressed = FALSE; - - return TRUE; -} - -static void -gtk_tree_view_put (GtkTreeView *tree_view, - GtkWidget *child_widget, - GtkTreePath *path, - GtkTreeViewColumn *column, - const GtkBorder *border) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreeViewChild *child; - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - g_return_if_fail (GTK_IS_WIDGET (child_widget)); - - child = g_slice_new (GtkTreeViewChild); - - child->widget = child_widget; - if (_gtk_tree_view_find_node (tree_view, - path, - &child->tree, - &child->node)) - { - g_assert_not_reached (); - } - child->column = column; - child->border = *border; - - priv->children = g_list_append (priv->children, child); - - gtk_css_node_insert_after (gtk_widget_get_css_node (GTK_WIDGET (tree_view)), - gtk_widget_get_css_node (child_widget), - priv->header_node); - gtk_widget_set_parent (child_widget, GTK_WIDGET (tree_view)); -} - -/* TreeModel Callbacks - */ - -static void -gtk_tree_view_row_changed (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data) -{ - GtkTreeView *tree_view = (GtkTreeView *)data; - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreeRBTree *tree; - GtkTreeRBNode *node; - gboolean free_path = FALSE; - GList *list; - GtkTreePath *cursor_path; - - g_return_if_fail (path != NULL || iter != NULL); - - if (priv->cursor_node != NULL) - cursor_path = _gtk_tree_path_new_from_rbtree (priv->cursor_tree, - priv->cursor_node); - else - cursor_path = NULL; - - if (priv->edited_column && - (cursor_path == NULL || gtk_tree_path_compare (cursor_path, path) == 0)) - gtk_tree_view_stop_editing (tree_view, TRUE); - - if (cursor_path != NULL) - gtk_tree_path_free (cursor_path); - - if (path == NULL) - { - path = gtk_tree_model_get_path (model, iter); - free_path = TRUE; - } - else if (iter == NULL) - gtk_tree_model_get_iter (model, iter, path); - - if (_gtk_tree_view_find_node (tree_view, - path, - &tree, - &node)) - /* We aren't actually showing the node */ - goto done; - - if (tree == NULL) - goto done; - - if (priv->fixed_height_mode - && priv->fixed_height >= 0) - { - gtk_tree_rbtree_node_set_height (tree, node, priv->fixed_height); - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - } - else - { - gtk_tree_rbtree_node_mark_invalid (tree, node); - for (list = priv->columns; list; list = list->next) - { - GtkTreeViewColumn *column; - - column = list->data; - if (!gtk_tree_view_column_get_visible (column)) - continue; - - if (gtk_tree_view_column_get_sizing (column) == GTK_TREE_VIEW_COLUMN_AUTOSIZE) - { - _gtk_tree_view_column_cell_set_dirty (column, TRUE); - } - } - } - - done: - if (!priv->fixed_height_mode && - gtk_widget_get_realized (GTK_WIDGET (tree_view))) - install_presize_handler (tree_view); - if (free_path) - gtk_tree_path_free (path); -} - -static void -gtk_tree_view_row_inserted (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data) -{ - GtkTreeView *tree_view = (GtkTreeView *) data; - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - int *indices; - GtkTreeRBTree *tree; - GtkTreeRBNode *tmpnode = NULL; - int depth; - int i = 0; - int height; - gboolean free_path = FALSE; - - g_return_if_fail (path != NULL || iter != NULL); - - if (priv->fixed_height_mode - && priv->fixed_height >= 0) - height = priv->fixed_height; - else - height = 0; - - if (path == NULL) - { - path = gtk_tree_model_get_path (model, iter); - free_path = TRUE; - } - else if (iter == NULL) - gtk_tree_model_get_iter (model, iter, path); - - if (priv->tree == NULL) - priv->tree = gtk_tree_rbtree_new (); - - tree = priv->tree; - - /* Update all row-references */ - gtk_tree_row_reference_inserted (G_OBJECT (data), path); - depth = gtk_tree_path_get_depth (path); - indices = gtk_tree_path_get_indices (path); - - /* First, find the parent tree */ - while (i < depth - 1) - { - if (tree == NULL) - { - /* We aren't showing the node */ - goto done; - } - - tmpnode = gtk_tree_rbtree_find_count (tree, indices[i] + 1); - if (tmpnode == NULL) - { - g_warning ("A node was inserted with a parent that's not in the tree.\n" \ - "This possibly means that a GtkTreeModel inserted a child node\n" \ - "before the parent was inserted."); - goto done; - } - else if (!GTK_TREE_RBNODE_FLAG_SET (tmpnode, GTK_TREE_RBNODE_IS_PARENT)) - { - /* FIXME enforce correct behavior on model, probably */ - /* In theory, the model should have emitted has_child_toggled here. We - * try to catch it anyway, just to be safe, in case the model hasn't. - */ - GtkTreePath *tmppath = _gtk_tree_path_new_from_rbtree (tree, tmpnode); - gtk_tree_view_row_has_child_toggled (model, tmppath, NULL, data); - gtk_tree_path_free (tmppath); - goto done; - } - - tree = tmpnode->children; - i++; - } - - if (tree == NULL) - { - goto done; - } - - /* ref the node */ - gtk_tree_model_ref_node (priv->model, iter); - if (indices[depth - 1] == 0) - { - tmpnode = gtk_tree_rbtree_find_count (tree, 1); - tmpnode = gtk_tree_rbtree_insert_before (tree, tmpnode, height, FALSE); - } - else - { - tmpnode = gtk_tree_rbtree_find_count (tree, indices[depth - 1]); - tmpnode = gtk_tree_rbtree_insert_after (tree, tmpnode, height, FALSE); - } - - done: - if (height > 0) - { - if (tree) - gtk_tree_rbtree_node_mark_valid (tree, tmpnode); - - gtk_widget_queue_resize (GTK_WIDGET (tree_view)); - } - else - install_presize_handler (tree_view); - if (free_path) - gtk_tree_path_free (path); -} - -static void -gtk_tree_view_row_has_child_toggled (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data) -{ - GtkTreeView *tree_view = (GtkTreeView *)data; - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreeIter real_iter; - gboolean has_child; - GtkTreeRBTree *tree; - GtkTreeRBNode *node; - gboolean free_path = FALSE; - - g_return_if_fail (path != NULL || iter != NULL); - - if (iter) - real_iter = *iter; - - if (path == NULL) - { - path = gtk_tree_model_get_path (model, iter); - free_path = TRUE; - } - else if (iter == NULL) - gtk_tree_model_get_iter (model, &real_iter, path); - - if (_gtk_tree_view_find_node (tree_view, - path, - &tree, - &node)) - /* We aren't actually showing the node */ - goto done; - - if (tree == NULL) - goto done; - - has_child = gtk_tree_model_iter_has_child (model, &real_iter); - /* Sanity check. - */ - if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_PARENT) == has_child) - goto done; - - if (has_child) - { - GTK_TREE_RBNODE_SET_FLAG (node, GTK_TREE_RBNODE_IS_PARENT); - } - else - { - GTK_TREE_RBNODE_UNSET_FLAG (node, GTK_TREE_RBNODE_IS_PARENT); - } - - if (has_child && priv->is_list) - { - priv->is_list = FALSE; - if (priv->show_expanders) - { - GList *list; - - for (list = priv->columns; list; list = list->next) - if (gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (list->data))) - { - _gtk_tree_view_column_cell_set_dirty (GTK_TREE_VIEW_COLUMN (list->data), TRUE); - break; - } - } - gtk_widget_queue_resize (GTK_WIDGET (tree_view)); - } - else - { - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - } - - done: - if (free_path) - gtk_tree_path_free (path); -} - -static void -check_selection_helper (GtkTreeRBTree *tree, - GtkTreeRBNode *node, - gpointer data) -{ - int *value = (int *)data; - - *value |= GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED); - - if (node->children && !*value) - gtk_tree_rbtree_traverse (node->children, node->children->root, G_POST_ORDER, check_selection_helper, data); -} - -static void -gtk_tree_view_row_deleted (GtkTreeModel *model, - GtkTreePath *path, - gpointer data) -{ - GtkTreeView *tree_view = (GtkTreeView *)data; - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreeRBTree *tree; - GtkTreeRBNode *node; - GList *list; - gboolean selection_changed = FALSE, cursor_changed = FALSE; - GtkTreeRBTree *cursor_tree = NULL; - GtkTreeRBNode *cursor_node = NULL; - - g_return_if_fail (path != NULL); - - gtk_tree_row_reference_deleted (G_OBJECT (data), path); - - if (_gtk_tree_view_find_node (tree_view, path, &tree, &node)) - return; - - if (tree == NULL) - return; - - /* check if the selection has been changed */ - gtk_tree_rbtree_traverse (tree, node, G_POST_ORDER, - check_selection_helper, &selection_changed); - - for (list = priv->columns; list; list = list->next) - if (gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (list->data)) && - gtk_tree_view_column_get_sizing (GTK_TREE_VIEW_COLUMN (list->data)) == GTK_TREE_VIEW_COLUMN_AUTOSIZE) - _gtk_tree_view_column_cell_set_dirty ((GtkTreeViewColumn *)list->data, TRUE); - - /* Ensure we don't have a dangling pointer to a dead node */ - ensure_unprelighted (tree_view); - - /* Cancel editing if we've started */ - gtk_tree_view_stop_editing (tree_view, TRUE); - - /* If the cursor row got deleted, move the cursor to the next row */ - if (priv->cursor_node && - (priv->cursor_node == node || - (node->children && (priv->cursor_tree == node->children || - gtk_tree_rbtree_contains (node->children, priv->cursor_tree))))) - { - GtkTreePath *cursor_path; - - cursor_tree = tree; - cursor_node = gtk_tree_rbtree_next (tree, node); - /* find the first node that is not going to be deleted */ - while (cursor_node == NULL && cursor_tree->parent_tree) - { - cursor_node = gtk_tree_rbtree_next (cursor_tree->parent_tree, - cursor_tree->parent_node); - cursor_tree = cursor_tree->parent_tree; - } - - if (cursor_node != NULL) - cursor_path = _gtk_tree_path_new_from_rbtree (cursor_tree, cursor_node); - else - cursor_path = NULL; - - if (cursor_path == NULL || - ! search_first_focusable_path (tree_view, &cursor_path, TRUE, - &cursor_tree, &cursor_node)) - { - /* It looks like we reached the end of the view without finding - * a focusable row. We will step backwards to find the last - * focusable row. - */ - gtk_tree_rbtree_prev_full (tree, node, &cursor_tree, &cursor_node); - if (cursor_node) - { - cursor_path = _gtk_tree_path_new_from_rbtree (cursor_tree, cursor_node); - if (! search_first_focusable_path (tree_view, &cursor_path, FALSE, - &cursor_tree, &cursor_node)) - cursor_node = NULL; - gtk_tree_path_free (cursor_path); - } - } - else if (cursor_path) - gtk_tree_path_free (cursor_path); - - cursor_changed = TRUE; - } - - if (tree->root->count == 1) - { - if (priv->tree == tree) - priv->tree = NULL; - - gtk_tree_rbtree_remove (tree); - } - else - { - gtk_tree_rbtree_remove_node (tree, node); - } - - if (! gtk_tree_row_reference_valid (priv->top_row)) - { - gtk_tree_row_reference_free (priv->top_row); - priv->top_row = NULL; - } - - install_scroll_sync_handler (tree_view); - - gtk_widget_queue_resize (GTK_WIDGET (tree_view)); - - if (cursor_changed) - { - if (cursor_node) - { - GtkTreePath *cursor_path = _gtk_tree_path_new_from_rbtree (cursor_tree, cursor_node); - gtk_tree_view_real_set_cursor (tree_view, cursor_path, CLEAR_AND_SELECT | CURSOR_INVALID); - gtk_tree_path_free (cursor_path); - } - else - gtk_tree_view_real_set_cursor (tree_view, NULL, CLEAR_AND_SELECT | CURSOR_INVALID); - } - if (selection_changed) - g_signal_emit_by_name (priv->selection, "changed"); -} - -static void -gtk_tree_view_rows_reordered (GtkTreeModel *model, - GtkTreePath *parent, - GtkTreeIter *iter, - int *new_order, - gpointer data) -{ - GtkTreeView *tree_view = GTK_TREE_VIEW (data); - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreeRBTree *tree; - GtkTreeRBNode *node; - int len; - - len = gtk_tree_model_iter_n_children (model, iter); - - if (len < 2) - return; - - gtk_tree_row_reference_reordered (G_OBJECT (data), - parent, - iter, - new_order); - - if (_gtk_tree_view_find_node (tree_view, - parent, - &tree, - &node)) - return; - - /* We need to special case the parent path */ - if (tree == NULL) - tree = priv->tree; - else - tree = node->children; - - if (tree == NULL) - return; - - if (priv->edited_column) - gtk_tree_view_stop_editing (tree_view, TRUE); - - /* we need to be unprelighted */ - ensure_unprelighted (tree_view); - - gtk_tree_rbtree_reorder (tree, new_order, len); - - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - - gtk_tree_view_dy_to_top_row (tree_view); -} - - -/* Internal tree functions - */ - - -static void -gtk_tree_view_get_background_xrange (GtkTreeView *tree_view, - GtkTreeRBTree *tree, - GtkTreeViewColumn *column, - int *x1, - int *x2) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreeViewColumn *tmp_column = NULL; - int total_width; - GList *list; - gboolean rtl; - - if (x1) - *x1 = 0; - - if (x2) - *x2 = 0; - - rtl = (_gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); - - total_width = 0; - for (list = (rtl ? g_list_last (priv->columns) : g_list_first (priv->columns)); - list; - list = (rtl ? list->prev : list->next)) - { - tmp_column = list->data; - - if (tmp_column == column) - break; - - if (gtk_tree_view_column_get_visible (tmp_column)) - total_width += gtk_tree_view_column_get_width (tmp_column); - } - - if (tmp_column != column) - { - g_warning (G_STRLOC": passed-in column isn't in the tree"); - return; - } - - if (x1) - *x1 = total_width; - - if (x2) - { - if (gtk_tree_view_column_get_visible (column)) - *x2 = total_width + gtk_tree_view_column_get_width (column); - else - *x2 = total_width; /* width of 0 */ - } -} - -static void -gtk_tree_view_get_arrow_xrange (GtkTreeView *tree_view, - GtkTreeRBTree *tree, - int *x1, - int *x2) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - int x_offset = 0; - GList *list; - GtkTreeViewColumn *tmp_column = NULL; - int total_width; - int expander_size, expander_render_size; - gboolean rtl; - - rtl = (_gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); - expander_size = gtk_tree_view_get_expander_size (tree_view); - expander_render_size = expander_size - (_TREE_VIEW_HORIZONTAL_SEPARATOR / 2); - - total_width = 0; - for (list = (rtl ? g_list_last (priv->columns) : g_list_first (priv->columns)); - list; - list = (rtl ? list->prev : list->next)) - { - tmp_column = list->data; - - if (gtk_tree_view_is_expander_column (tree_view, tmp_column)) - { - if (rtl) - x_offset = total_width + gtk_tree_view_column_get_width (tmp_column) - expander_size; - else - x_offset = total_width; - break; - } - - if (gtk_tree_view_column_get_visible (tmp_column)) - total_width += gtk_tree_view_column_get_width (tmp_column); - } - - x_offset += (expander_size - expander_render_size); - - if (rtl) - x_offset -= expander_size * gtk_tree_rbtree_get_depth (tree); - else - x_offset += expander_size * gtk_tree_rbtree_get_depth (tree); - - *x1 = x_offset; - - if (tmp_column && - gtk_tree_view_column_get_visible (tmp_column)) - /* +1 because x2 isn't included in the range. */ - *x2 = *x1 + expander_render_size + 1; - else - *x2 = *x1; -} - -static void -gtk_tree_view_build_tree (GtkTreeView *tree_view, - GtkTreeRBTree *tree, - GtkTreeIter *iter, - int depth, - gboolean recurse) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreeRBNode *temp = NULL; - GtkTreePath *path = NULL; - - do - { - gtk_tree_model_ref_node (priv->model, iter); - temp = gtk_tree_rbtree_insert_after (tree, temp, 0, FALSE); - - if (priv->fixed_height > 0) - { - if (GTK_TREE_RBNODE_FLAG_SET (temp, GTK_TREE_RBNODE_INVALID)) - { - gtk_tree_rbtree_node_set_height (tree, temp, priv->fixed_height); - gtk_tree_rbtree_node_mark_valid (tree, temp); - } - } - - if (priv->is_list) - continue; - - if (recurse) - { - GtkTreeIter child; - - if (!path) - path = gtk_tree_model_get_path (priv->model, iter); - else - gtk_tree_path_next (path); - - if (gtk_tree_model_iter_has_child (priv->model, iter)) - { - gboolean expand; - - g_signal_emit (tree_view, tree_view_signals[TEST_EXPAND_ROW], 0, iter, path, &expand); - - if (gtk_tree_model_iter_children (priv->model, &child, iter) - && !expand) - { - temp->children = gtk_tree_rbtree_new (); - temp->children->parent_tree = tree; - temp->children->parent_node = temp; - gtk_tree_view_build_tree (tree_view, temp->children, &child, depth + 1, recurse); - } - } - } - - if (gtk_tree_model_iter_has_child (priv->model, iter)) - { - if ((temp->flags>K_TREE_RBNODE_IS_PARENT) != GTK_TREE_RBNODE_IS_PARENT) - temp->flags ^= GTK_TREE_RBNODE_IS_PARENT; - } - } - while (gtk_tree_model_iter_next (priv->model, iter)); - - if (path) - gtk_tree_path_free (path); -} - -/* Make sure the node is visible vertically */ -static void -gtk_tree_view_clamp_node_visible (GtkTreeView *tree_view, - GtkTreeRBTree *tree, - GtkTreeRBNode *node) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - int node_dy, height; - GtkTreePath *path = NULL; - - if (!gtk_widget_get_realized (GTK_WIDGET (tree_view))) - return; - - /* just return if the node is visible, avoiding a costly expose */ - node_dy = gtk_tree_rbtree_node_find_offset (tree, node); - height = gtk_tree_view_get_row_height (tree_view, node); - if (! GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_INVALID) - && node_dy >= gtk_adjustment_get_value (priv->vadjustment) - && node_dy + height <= (gtk_adjustment_get_value (priv->vadjustment) - + gtk_adjustment_get_page_size (priv->vadjustment))) - return; - - path = _gtk_tree_path_new_from_rbtree (tree, node); - if (path) - { - gtk_tree_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0.0, 0.0); - gtk_tree_path_free (path); - } -} - -static void -gtk_tree_view_clamp_column_visible (GtkTreeView *tree_view, - GtkTreeViewColumn *column, - gboolean focus_to_cell) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkAllocation allocation; - int x, width; - - if (column == NULL) - return; - - gtk_widget_get_allocation (gtk_tree_view_column_get_button (column), &allocation); - x = allocation.x; - width = allocation.width; - - if (width > gtk_adjustment_get_page_size (priv->hadjustment)) - { - /* The column is larger than the horizontal page size. If the - * column has cells which can be focused individually, then we make - * sure the cell which gets focus is fully visible (if even the - * focus cell is bigger than the page size, we make sure the - * left-hand side of the cell is visible). - * - * If the column does not have an activatable cell, we - * make sure the left-hand side of the column is visible. - */ - - if (focus_to_cell && gtk_tree_view_has_can_focus_cell (tree_view)) - { - GtkCellArea *cell_area; - GtkCellRenderer *focus_cell; - - cell_area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (column)); - focus_cell = gtk_cell_area_get_focus_cell (cell_area); - - if (gtk_tree_view_column_cell_get_position (column, focus_cell, - &x, &width)) - { - if (width < gtk_adjustment_get_page_size (priv->hadjustment)) - { - if (gtk_adjustment_get_value (priv->hadjustment) + gtk_adjustment_get_page_size (priv->hadjustment) < x + width) - gtk_adjustment_set_value (priv->hadjustment, - x + width - gtk_adjustment_get_page_size (priv->hadjustment)); - else if (gtk_adjustment_get_value (priv->hadjustment) > x) - gtk_adjustment_set_value (priv->hadjustment, x); - } - } - } - - gtk_adjustment_set_value (priv->hadjustment, x); - } - else - { - if ((gtk_adjustment_get_value (priv->hadjustment) + gtk_adjustment_get_page_size (priv->hadjustment)) < (x + width)) - gtk_adjustment_set_value (priv->hadjustment, - x + width - gtk_adjustment_get_page_size (priv->hadjustment)); - else if (gtk_adjustment_get_value (priv->hadjustment) > x) - gtk_adjustment_set_value (priv->hadjustment, x); - } -} - -/* This function could be more efficient. I'll optimize it if profiling seems - * to imply that it is important */ -GtkTreePath * -_gtk_tree_path_new_from_rbtree (GtkTreeRBTree *tree, - GtkTreeRBNode *node) -{ - GtkTreePath *path; - GtkTreeRBTree *tmp_tree; - GtkTreeRBNode *tmp_node, *last; - int count; - - path = gtk_tree_path_new (); - - g_return_val_if_fail (node != NULL, path); - - count = 1 + node->left->count; - - last = node; - tmp_node = node->parent; - tmp_tree = tree; - while (tmp_tree) - { - while (!gtk_tree_rbtree_is_nil (tmp_node)) - { - if (tmp_node->right == last) - count += 1 + tmp_node->left->count; - last = tmp_node; - tmp_node = tmp_node->parent; - } - gtk_tree_path_prepend_index (path, count - 1); - last = tmp_tree->parent_node; - tmp_tree = tmp_tree->parent_tree; - if (last) - { - count = 1 + last->left->count; - tmp_node = last->parent; - } - } - return path; -} - -/* Returns TRUE if we ran out of tree before finding the path. If the path is - * invalid (ie. points to a node that’s not in the tree), *tree and *node are - * both set to NULL. - */ -gboolean -_gtk_tree_view_find_node (GtkTreeView *tree_view, - GtkTreePath *path, - GtkTreeRBTree **tree, - GtkTreeRBNode **node) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreeRBNode *tmpnode = NULL; - GtkTreeRBTree *tmptree = priv->tree; - int *indices = gtk_tree_path_get_indices (path); - int depth = gtk_tree_path_get_depth (path); - int i = 0; - - *node = NULL; - *tree = NULL; - - if (depth == 0 || tmptree == NULL) - return FALSE; - do - { - tmpnode = gtk_tree_rbtree_find_count (tmptree, indices[i] + 1); - ++i; - if (tmpnode == NULL) - { - *tree = NULL; - *node = NULL; - return FALSE; - } - if (i >= depth) - { - *tree = tmptree; - *node = tmpnode; - return FALSE; - } - *tree = tmptree; - *node = tmpnode; - tmptree = tmpnode->children; - if (tmptree == NULL) - return TRUE; - } - while (1); -} - -static gboolean -gtk_tree_view_is_expander_column (GtkTreeView *tree_view, - GtkTreeViewColumn *column) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GList *list; - - if (priv->is_list) - return FALSE; - - if (priv->expander_column != NULL) - { - if (priv->expander_column == column) - return TRUE; - return FALSE; - } - else - { - for (list = priv->columns; - list; - list = list->next) - if (gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (list->data))) - break; - if (list && list->data == column) - return TRUE; - } - return FALSE; -} - -static inline gboolean -gtk_tree_view_draw_expanders (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - if (!priv->is_list && priv->show_expanders) - return TRUE; - /* else */ - return FALSE; -} - -static void -gtk_tree_view_add_move_binding (GtkWidgetClass *widget_class, - guint keyval, - guint modmask, - gboolean add_shifted_binding, - GtkMovementStep step, - int count) -{ - gtk_widget_class_add_binding_signal (widget_class, - keyval, modmask, - "move-cursor", - "(iibb)", step, count, FALSE, FALSE); - - if (add_shifted_binding) - gtk_widget_class_add_binding_signal (widget_class, - keyval, GDK_SHIFT_MASK, - "move-cursor", - "(iibb)", step, count, TRUE, FALSE); - - if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK) - return; - - gtk_widget_class_add_binding_signal (widget_class, - keyval, GDK_CONTROL_MASK, - "move-cursor", - "(iibb)", step, count, FALSE, TRUE); - - if (add_shifted_binding) - gtk_widget_class_add_binding_signal (widget_class, keyval, - GDK_CONTROL_MASK | GDK_SHIFT_MASK, - "move-cursor", - "(iibb)", step, count, TRUE, TRUE); -} - -static int -gtk_tree_view_unref_tree_helper (GtkTreeModel *model, - GtkTreeIter *iter, - GtkTreeRBTree *tree, - GtkTreeRBNode *node) -{ - int retval = FALSE; - do - { - g_return_val_if_fail (node != NULL, FALSE); - - if (node->children) - { - GtkTreeIter child; - GtkTreeRBTree *new_tree; - GtkTreeRBNode *new_node; - - new_tree = node->children; - new_node = gtk_tree_rbtree_first (new_tree); - - if (!gtk_tree_model_iter_children (model, &child, iter)) - return FALSE; - - retval = gtk_tree_view_unref_tree_helper (model, &child, new_tree, new_node) | retval; - } - - if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED)) - retval = TRUE; - gtk_tree_model_unref_node (model, iter); - node = gtk_tree_rbtree_next (tree, node); - } - while (gtk_tree_model_iter_next (model, iter)); - - return retval; -} - -static int -gtk_tree_view_unref_and_check_selection_tree (GtkTreeView *tree_view, - GtkTreeRBTree *tree) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreeIter iter; - GtkTreePath *path; - GtkTreeRBNode *node; - int retval; - - if (!tree) - return FALSE; - - node = gtk_tree_rbtree_first (tree); - - g_return_val_if_fail (node != NULL, FALSE); - path = _gtk_tree_path_new_from_rbtree (tree, node); - gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->model), - &iter, path); - retval = gtk_tree_view_unref_tree_helper (GTK_TREE_MODEL (priv->model), &iter, tree, node); - gtk_tree_path_free (path); - - return retval; -} - -static void -gtk_tree_view_set_column_drag_info (GtkTreeView *tree_view, - GtkTreeViewColumn *column) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreeViewColumn *left_column; - GtkTreeViewColumn *cur_column = NULL; - GtkTreeViewColumnReorder *reorder; - gboolean rtl; - GList *tmp_list; - int left; - - /* We want to precalculate the motion list such that we know what column slots - * are available. - */ - left_column = NULL; - rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); - - /* First, identify all possible drop spots */ - if (rtl) - tmp_list = g_list_last (priv->columns); - else - tmp_list = g_list_first (priv->columns); - - while (tmp_list) - { - cur_column = GTK_TREE_VIEW_COLUMN (tmp_list->data); - tmp_list = rtl ? tmp_list->prev : tmp_list->next; - - if (gtk_tree_view_column_get_visible (cur_column) == FALSE) - continue; - - /* If it's not the column moving and func tells us to skip over the column, we continue. */ - if (left_column != column && cur_column != column && - priv->column_drop_func && - ! priv->column_drop_func (tree_view, column, left_column, cur_column, priv->column_drop_func_data)) - { - left_column = cur_column; - continue; - } - reorder = g_slice_new0 (GtkTreeViewColumnReorder); - reorder->left_column = left_column; - left_column = reorder->right_column = cur_column; - - priv->column_drag_info = g_list_append (priv->column_drag_info, reorder); - } - - /* Add the last one */ - if (priv->column_drop_func == NULL || - ((left_column != column) && - priv->column_drop_func (tree_view, column, left_column, NULL, priv->column_drop_func_data))) - { - reorder = g_slice_new0 (GtkTreeViewColumnReorder); - reorder->left_column = left_column; - reorder->right_column = NULL; - priv->column_drag_info = g_list_append (priv->column_drag_info, reorder); - } - - /* We quickly check to see if it even makes sense to reorder columns. */ - /* If there is nothing that can be moved, then we return */ - - if (priv->column_drag_info == NULL) - return; - - /* We know there are always 2 slots possbile, as you can always return column. */ - /* If that's all there is, return */ - if (priv->column_drag_info->next == NULL || - (priv->column_drag_info->next->next == NULL && - ((GtkTreeViewColumnReorder *)priv->column_drag_info->data)->right_column == column && - ((GtkTreeViewColumnReorder *)priv->column_drag_info->next->data)->left_column == column)) - { - for (tmp_list = priv->column_drag_info; tmp_list; tmp_list = tmp_list->next) - g_slice_free (GtkTreeViewColumnReorder, tmp_list->data); - g_list_free (priv->column_drag_info); - priv->column_drag_info = NULL; - return; - } - /* We fill in the ranges for the columns, now that we've isolated them */ - left = - TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view); - - for (tmp_list = priv->column_drag_info; tmp_list; tmp_list = tmp_list->next) - { - reorder = (GtkTreeViewColumnReorder *) tmp_list->data; - - reorder->left_align = left; - if (tmp_list->next != NULL) - { - GtkAllocation right_allocation, left_allocation; - GtkWidget *left_button, *right_button; - - g_assert (tmp_list->next->data); - - right_button = gtk_tree_view_column_get_button (reorder->right_column); - left_button = gtk_tree_view_column_get_button - (((GtkTreeViewColumnReorder *)tmp_list->next->data)->left_column); - - gtk_widget_get_allocation (right_button, &right_allocation); - gtk_widget_get_allocation (left_button, &left_allocation); - left = reorder->right_align = (right_allocation.x + right_allocation.width + left_allocation.x) / 2; - } - else - { - reorder->right_align = gtk_widget_get_allocated_width (GTK_WIDGET (tree_view)) - + TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view); - } - } -} - -void -_gtk_tree_view_column_start_drag (GtkTreeView *tree_view, - GtkTreeViewColumn *column, - GdkDevice *device) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkAllocation button_allocation; - GtkWidget *button; - GtkStyleContext *context; - - g_return_if_fail (priv->column_drag_info == NULL); - g_return_if_fail (priv->cur_reorder == NULL); - - gtk_tree_view_set_column_drag_info (tree_view, column); - - if (priv->column_drag_info == NULL) - return; - - button = gtk_tree_view_column_get_button (column); - - context = gtk_widget_get_style_context (button); - gtk_style_context_add_class (context, "dnd"); - - gtk_widget_get_allocation (button, &button_allocation); - priv->drag_column_x = button_allocation.x; - priv->drag_column_y = button_allocation.y; - - priv->drag_column = column; - - gtk_widget_grab_focus (GTK_WIDGET (tree_view)); - - priv->in_column_drag = TRUE; - - gtk_gesture_set_state (priv->column_drag_gesture, - GTK_EVENT_SEQUENCE_CLAIMED); -} - -static inline int -gtk_tree_view_get_effective_header_height (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - if (priv->headers_visible) - return priv->header_height; - else - return 0; -} - -void -_gtk_tree_view_get_row_separator_func (GtkTreeView *tree_view, - GtkTreeViewRowSeparatorFunc *func, - gpointer *data) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - *func = priv->row_separator_func; - *data = priv->row_separator_data; -} - -GtkTreePath * -_gtk_tree_view_get_anchor_path (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - if (priv->anchor) - return gtk_tree_row_reference_get_path (priv->anchor); - - return NULL; -} - -void -_gtk_tree_view_set_anchor_path (GtkTreeView *tree_view, - GtkTreePath *anchor_path) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - if (priv->anchor) - { - gtk_tree_row_reference_free (priv->anchor); - priv->anchor = NULL; - } - - if (anchor_path && priv->model) - priv->anchor = - gtk_tree_row_reference_new (priv->model, anchor_path); -} - -GtkTreeRBTree * -_gtk_tree_view_get_rbtree (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - return priv->tree; -} - -gboolean -_gtk_tree_view_get_cursor_node (GtkTreeView *tree_view, - GtkTreeRBTree **tree, - GtkTreeRBNode **node) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - if (priv->cursor_node == NULL) - return FALSE; - - *tree = priv->cursor_tree; - *node = priv->cursor_node; - - return TRUE; -} - -GtkTreeViewColumn * -_gtk_tree_view_get_focus_column (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - return priv->focus_column; -} - -void -_gtk_tree_view_set_focus_column (GtkTreeView *tree_view, - GtkTreeViewColumn *column) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreeViewColumn *old_column = priv->focus_column; - - if (old_column == column) - return; - - priv->focus_column = column; -} - -/* x and y are the mouse position - */ -static void -gtk_tree_view_snapshot_arrow (GtkTreeView *tree_view, - GtkSnapshot *snapshot, - GtkTreeRBTree *tree, - GtkTreeRBNode *node) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GdkRectangle area; - GtkStateFlags state = 0; - GtkStyleContext *context; - GtkWidget *widget; - int x_offset = 0; - int x2; - GtkCellRendererState flags = 0; - - widget = GTK_WIDGET (tree_view); - context = gtk_widget_get_style_context (widget); - - if (! GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_PARENT)) - return; - - gtk_tree_view_get_arrow_xrange (tree_view, tree, &x_offset, &x2); - - area.x = x_offset; - area.y = gtk_tree_view_get_cell_area_y_offset (tree_view, tree, node); - area.width = x2 - x_offset; - area.height = gtk_tree_view_get_cell_area_height (tree_view, node); - - if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED)) - flags |= GTK_CELL_RENDERER_SELECTED; - - if (node == priv->prelight_node && - priv->arrow_prelit) - flags |= GTK_CELL_RENDERER_PRELIT; - - state = gtk_cell_renderer_get_state (NULL, widget, flags); - - if (node->children != NULL) - state |= GTK_STATE_FLAG_CHECKED; - else - state &= ~(GTK_STATE_FLAG_CHECKED); - - gtk_style_context_save (context); - - gtk_style_context_set_state (context, state); - gtk_style_context_add_class (context, "expander"); - - gtk_snapshot_save (snapshot); - gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (area.x, area.y)); - gtk_css_style_snapshot_icon (gtk_style_context_lookup_style (context), snapshot, - area.width, area.height); - gtk_snapshot_restore (snapshot); - - gtk_style_context_restore (context); -} - -static void -gtk_tree_view_focus_to_cursor (GtkTreeView *tree_view) - -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreePath *cursor_path; - - if ((priv->tree == NULL) || - (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))) - return; - - cursor_path = NULL; - if (priv->cursor_node) - cursor_path = _gtk_tree_path_new_from_rbtree (priv->cursor_tree, - priv->cursor_node); - - if (cursor_path == NULL) - { - /* Consult the selection before defaulting to the - * first focusable element - */ - GList *selected_rows; - GtkTreeModel *model; - GtkTreeSelection *selection; - - selection = gtk_tree_view_get_selection (tree_view); - selected_rows = gtk_tree_selection_get_selected_rows (selection, &model); - - if (selected_rows) - { - cursor_path = gtk_tree_path_copy((const GtkTreePath *)(selected_rows->data)); - g_list_free_full (selected_rows, (GDestroyNotify) gtk_tree_path_free); - } - else - { - cursor_path = gtk_tree_path_new_first (); - search_first_focusable_path (tree_view, &cursor_path, - TRUE, NULL, NULL); - } - - if (cursor_path) - { - if (gtk_tree_selection_get_mode (priv->selection) == GTK_SELECTION_MULTIPLE) - gtk_tree_view_real_set_cursor (tree_view, cursor_path, 0); - else - gtk_tree_view_real_set_cursor (tree_view, cursor_path, CLEAR_AND_SELECT); - } - } - - if (cursor_path) - { - priv->draw_keyfocus = TRUE; - - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - gtk_tree_path_free (cursor_path); - - if (priv->focus_column == NULL) - { - GList *list; - for (list = priv->columns; list; list = list->next) - { - if (gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (list->data))) - { - GtkCellArea *cell_area; - - _gtk_tree_view_set_focus_column (tree_view, GTK_TREE_VIEW_COLUMN (list->data)); - - /* This happens when the treeview initially grabs focus and there - * is no column in focus, here we explicitly focus into the first cell */ - cell_area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (priv->focus_column)); - if (!gtk_cell_area_get_focus_cell (cell_area)) - { - gboolean rtl; - - rtl = (_gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); - gtk_cell_area_focus (cell_area, - rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT); - } - - break; - } - } - } - } -} - -static void -gtk_tree_view_move_cursor_up_down (GtkTreeView *tree_view, - int count) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - int selection_count; - GtkTreeRBTree *new_cursor_tree = NULL; - GtkTreeRBNode *new_cursor_node = NULL; - GtkTreePath *cursor_path = NULL; - gboolean selectable; - GtkDirectionType direction; - GtkCellArea *cell_area = NULL; - GtkCellRenderer *last_focus_cell = NULL; - GtkTreeIter iter; - - if (priv->cursor_node == NULL) - return; - - cursor_path = _gtk_tree_path_new_from_rbtree (priv->cursor_tree, - priv->cursor_node); - - direction = count < 0 ? GTK_DIR_UP : GTK_DIR_DOWN; - - if (priv->focus_column) - cell_area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (priv->focus_column)); - - /* If focus stays in the area for this row, then just return for this round */ - if (cell_area && (count == -1 || count == 1) && - gtk_tree_model_get_iter (priv->model, &iter, cursor_path)) - { - gtk_tree_view_column_cell_set_cell_data (priv->focus_column, - priv->model, - &iter, - GTK_TREE_RBNODE_FLAG_SET (priv->cursor_node, GTK_TREE_RBNODE_IS_PARENT), - priv->cursor_node->children ? TRUE : FALSE); - - /* Save the last cell that had focus, if we hit the end of the view we'll give - * focus back to it. */ - last_focus_cell = gtk_cell_area_get_focus_cell (cell_area); - - /* If focus stays in the area, no need to change the cursor row */ - if (gtk_cell_area_focus (cell_area, direction)) - return; - } - - selection_count = gtk_tree_selection_count_selected_rows (priv->selection); - selectable = _gtk_tree_selection_row_is_selectable (priv->selection, - priv->cursor_node, - cursor_path); - - if (selection_count == 0 - && gtk_tree_selection_get_mode (priv->selection) != GTK_SELECTION_NONE - && !priv->modify_selection_pressed - && selectable) - { - /* Don't move the cursor, but just select the current node */ - new_cursor_tree = priv->cursor_tree; - new_cursor_node = priv->cursor_node; - } - else - { - if (count == -1) - gtk_tree_rbtree_prev_full (priv->cursor_tree, priv->cursor_node, - &new_cursor_tree, &new_cursor_node); - else - gtk_tree_rbtree_next_full (priv->cursor_tree, priv->cursor_node, - &new_cursor_tree, &new_cursor_node); - } - - gtk_tree_path_free (cursor_path); - - if (new_cursor_node) - { - cursor_path = _gtk_tree_path_new_from_rbtree (new_cursor_tree, new_cursor_node); - - search_first_focusable_path (tree_view, &cursor_path, - (count != -1), - &new_cursor_tree, - &new_cursor_node); - - if (cursor_path) - gtk_tree_path_free (cursor_path); - } - - /* - * If the list has only one item and multi-selection is set then select - * the row (if not yet selected). - */ - if (gtk_tree_selection_get_mode (priv->selection) == GTK_SELECTION_MULTIPLE && - new_cursor_node == NULL) - { - if (count == -1) - gtk_tree_rbtree_next_full (priv->cursor_tree, priv->cursor_node, - &new_cursor_tree, &new_cursor_node); - else - gtk_tree_rbtree_prev_full (priv->cursor_tree, priv->cursor_node, - &new_cursor_tree, &new_cursor_node); - - if (new_cursor_node == NULL - && !GTK_TREE_RBNODE_FLAG_SET (priv->cursor_node, GTK_TREE_RBNODE_IS_SELECTED)) - { - new_cursor_node = priv->cursor_node; - new_cursor_tree = priv->cursor_tree; - } - else - { - new_cursor_tree = NULL; - new_cursor_node = NULL; - } - } - - if (new_cursor_node) - { - cursor_path = _gtk_tree_path_new_from_rbtree (new_cursor_tree, new_cursor_node); - gtk_tree_view_real_set_cursor (tree_view, cursor_path, CLEAR_AND_SELECT | CLAMP_NODE); - gtk_tree_path_free (cursor_path); - - /* Give focus to the area in the new row */ - if (cell_area) - gtk_cell_area_focus (cell_area, direction); - } - else - { - gtk_tree_view_clamp_node_visible (tree_view, - priv->cursor_tree, - priv->cursor_node); - - if (!priv->extend_selection_pressed) - { - if (! gtk_widget_keynav_failed (GTK_WIDGET (tree_view), - count < 0 ? - GTK_DIR_UP : GTK_DIR_DOWN)) - { - GtkWidget *toplevel = GTK_WIDGET (gtk_widget_get_root (GTK_WIDGET (tree_view))); - - if (toplevel) - gtk_widget_child_focus (toplevel, - count < 0 ? - GTK_DIR_TAB_BACKWARD : - GTK_DIR_TAB_FORWARD); - } - } - else - { - gtk_widget_error_bell (GTK_WIDGET (tree_view)); - } - - if (cell_area) - gtk_cell_area_set_focus_cell (cell_area, last_focus_cell); - } -} - -static void -gtk_tree_view_move_cursor_page_up_down (GtkTreeView *tree_view, - int count) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreePath *old_cursor_path = NULL; - GtkTreePath *cursor_path = NULL; - GtkTreeRBTree *start_cursor_tree = NULL; - GtkTreeRBNode *start_cursor_node = NULL; - GtkTreeRBTree *cursor_tree; - GtkTreeRBNode *cursor_node; - int y; - int window_y; - - if (!gtk_widget_has_focus (GTK_WIDGET (tree_view))) - return; - - if (priv->cursor_node == NULL) - return; - - old_cursor_path = _gtk_tree_path_new_from_rbtree (priv->cursor_tree, - priv->cursor_node); - - y = gtk_tree_rbtree_node_find_offset (priv->cursor_tree, priv->cursor_node); - window_y = RBTREE_Y_TO_TREE_WINDOW_Y (priv, y); - y += priv->cursor_offset; - y += count * (int)gtk_adjustment_get_page_increment (priv->vadjustment); - y = CLAMP (y, (int)gtk_adjustment_get_lower (priv->vadjustment), (int)gtk_adjustment_get_upper (priv->vadjustment)); - - if (y >= gtk_tree_view_get_height (tree_view)) - y = gtk_tree_view_get_height (tree_view) - 1; - - priv->cursor_offset = - gtk_tree_rbtree_find_offset (priv->tree, y, - &cursor_tree, &cursor_node); - - if (cursor_tree == NULL) - { - /* FIXME: we lost the cursor. Should we try to get one? */ - gtk_tree_path_free (old_cursor_path); - return; - } - - if (priv->cursor_offset - > gtk_tree_view_get_row_height (tree_view, cursor_node)) - { - gtk_tree_rbtree_next_full (cursor_tree, cursor_node, - &cursor_tree, &cursor_node); - priv->cursor_offset -= gtk_tree_view_get_row_height (tree_view, cursor_node); - } - - y -= priv->cursor_offset; - cursor_path = _gtk_tree_path_new_from_rbtree (cursor_tree, cursor_node); - - start_cursor_tree = cursor_tree; - start_cursor_node = cursor_node; - - if (! search_first_focusable_path (tree_view, &cursor_path, - (count != -1), - &cursor_tree, &cursor_node)) - { - /* It looks like we reached the end of the view without finding - * a focusable row. We will step backwards to find the last - * focusable row. - */ - cursor_tree = start_cursor_tree; - cursor_node = start_cursor_node; - cursor_path = _gtk_tree_path_new_from_rbtree (cursor_tree, cursor_node); - - search_first_focusable_path (tree_view, &cursor_path, - (count == -1), - &cursor_tree, &cursor_node); - } - - if (!cursor_path) - goto cleanup; - - /* update y */ - y = gtk_tree_rbtree_node_find_offset (cursor_tree, cursor_node); - - gtk_tree_view_real_set_cursor (tree_view, cursor_path, CLEAR_AND_SELECT); - - y -= window_y; - gtk_tree_view_scroll_to_point (tree_view, -1, y); - gtk_tree_view_clamp_node_visible (tree_view, cursor_tree, cursor_node); - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - - if (!gtk_tree_path_compare (old_cursor_path, cursor_path)) - gtk_widget_error_bell (GTK_WIDGET (tree_view)); - - gtk_widget_grab_focus (GTK_WIDGET (tree_view)); - -cleanup: - gtk_tree_path_free (old_cursor_path); - gtk_tree_path_free (cursor_path); -} - -static void -gtk_tree_view_move_cursor_left_right (GtkTreeView *tree_view, - int count) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreePath *cursor_path = NULL; - GtkTreeViewColumn *column; - GtkTreeIter iter; - GList *list; - gboolean found_column = FALSE; - gboolean rtl; - GtkDirectionType direction; - GtkCellArea *cell_area; - GtkCellRenderer *last_focus_cell = NULL; - GtkCellArea *last_focus_area = NULL; - - rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); - - if (!gtk_widget_has_focus (GTK_WIDGET (tree_view))) - return; - - if (priv->cursor_node == NULL) - return; - - cursor_path = _gtk_tree_path_new_from_rbtree (priv->cursor_tree, - priv->cursor_node); - - if (gtk_tree_model_get_iter (priv->model, &iter, cursor_path) == FALSE) - { - gtk_tree_path_free (cursor_path); - return; - } - gtk_tree_path_free (cursor_path); - - list = rtl ? g_list_last (priv->columns) : g_list_first (priv->columns); - if (priv->focus_column) - { - /* Save the cell/area we are moving focus from, if moving the cursor - * by one step hits the end we'll set focus back here */ - last_focus_area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (priv->focus_column)); - last_focus_cell = gtk_cell_area_get_focus_cell (last_focus_area); - - for (; list; list = (rtl ? list->prev : list->next)) - { - if (list->data == priv->focus_column) - break; - } - } - - direction = count > 0 ? GTK_DIR_RIGHT : GTK_DIR_LEFT; - - while (list) - { - column = list->data; - if (gtk_tree_view_column_get_visible (column) == FALSE) - goto loop_end; - - gtk_tree_view_column_cell_set_cell_data (column, - priv->model, - &iter, - GTK_TREE_RBNODE_FLAG_SET (priv->cursor_node, GTK_TREE_RBNODE_IS_PARENT), - priv->cursor_node->children ? TRUE : FALSE); - - cell_area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (column)); - if (gtk_cell_area_focus (cell_area, direction)) - { - _gtk_tree_view_set_focus_column (tree_view, column); - found_column = TRUE; - break; - } - - loop_end: - if (count == 1) - list = rtl ? list->prev : list->next; - else - list = rtl ? list->next : list->prev; - } - - if (found_column) - { - if (!gtk_tree_view_has_can_focus_cell (tree_view)) - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0); - gtk_widget_grab_focus (GTK_WIDGET (tree_view)); - } - else - { - gtk_widget_error_bell (GTK_WIDGET (tree_view)); - - if (last_focus_area) - gtk_cell_area_set_focus_cell (last_focus_area, last_focus_cell); - } - - gtk_tree_view_clamp_column_visible (tree_view, - priv->focus_column, TRUE); -} - -static void -gtk_tree_view_move_cursor_start_end (GtkTreeView *tree_view, - int count) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreeRBTree *cursor_tree; - GtkTreeRBNode *cursor_node; - GtkTreePath *path; - GtkTreePath *old_path; - - if (!gtk_widget_has_focus (GTK_WIDGET (tree_view))) - return; - - g_return_if_fail (priv->tree != NULL); - - gtk_tree_view_get_cursor (tree_view, &old_path, NULL); - - cursor_tree = priv->tree; - - if (count == -1) - { - cursor_node = gtk_tree_rbtree_first (cursor_tree); - - /* Now go forward to find the first focusable row. */ - path = _gtk_tree_path_new_from_rbtree (cursor_tree, cursor_node); - search_first_focusable_path (tree_view, &path, - TRUE, &cursor_tree, &cursor_node); - } - else - { - cursor_node = cursor_tree->root; - - do - { - while (cursor_node && !gtk_tree_rbtree_is_nil (cursor_node->right)) - cursor_node = cursor_node->right; - if (cursor_node->children == NULL) - break; - - cursor_tree = cursor_node->children; - cursor_node = cursor_tree->root; - } - while (1); - - /* Now go backwards to find last focusable row. */ - path = _gtk_tree_path_new_from_rbtree (cursor_tree, cursor_node); - search_first_focusable_path (tree_view, &path, - FALSE, &cursor_tree, &cursor_node); - } - - if (!path) - goto cleanup; - - if (gtk_tree_path_compare (old_path, path)) - { - gtk_tree_view_real_set_cursor (tree_view, path, CLEAR_AND_SELECT | CLAMP_NODE); - gtk_widget_grab_focus (GTK_WIDGET (tree_view)); - } - else - { - gtk_widget_error_bell (GTK_WIDGET (tree_view)); - } - -cleanup: - gtk_tree_path_free (old_path); - gtk_tree_path_free (path); -} - -static gboolean -gtk_tree_view_real_select_all (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - if (!gtk_widget_has_focus (GTK_WIDGET (tree_view))) - return FALSE; - - if (gtk_tree_selection_get_mode (priv->selection) != GTK_SELECTION_MULTIPLE) - return FALSE; - - gtk_tree_selection_select_all (priv->selection); - - return TRUE; -} - -static gboolean -gtk_tree_view_real_unselect_all (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - if (!gtk_widget_has_focus (GTK_WIDGET (tree_view))) - return FALSE; - - if (gtk_tree_selection_get_mode (priv->selection) != GTK_SELECTION_MULTIPLE) - return FALSE; - - gtk_tree_selection_unselect_all (priv->selection); - - return TRUE; -} - -static gboolean -gtk_tree_view_real_select_cursor_row (GtkTreeView *tree_view, - gboolean start_editing) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreeRBTree *new_tree = NULL; - GtkTreeRBNode *new_node = NULL; - GtkTreeRBTree *cursor_tree = NULL; - GtkTreeRBNode *cursor_node = NULL; - GtkTreePath *cursor_path = NULL; - GtkTreeSelectMode mode = 0; - - if (!gtk_widget_has_focus (GTK_WIDGET (tree_view))) - return FALSE; - - if (priv->cursor_node == NULL) - return FALSE; - - cursor_path = _gtk_tree_path_new_from_rbtree (priv->cursor_tree, - priv->cursor_node); - - _gtk_tree_view_find_node (tree_view, cursor_path, - &cursor_tree, &cursor_node); - - if (cursor_tree == NULL) - { - gtk_tree_path_free (cursor_path); - return FALSE; - } - - if (!priv->extend_selection_pressed && start_editing && - priv->focus_column) - { - if (gtk_tree_view_start_editing (tree_view, cursor_path, FALSE)) - { - gtk_tree_path_free (cursor_path); - return TRUE; - } - } - - if (priv->modify_selection_pressed) - mode |= GTK_TREE_SELECT_MODE_TOGGLE; - if (priv->extend_selection_pressed) - mode |= GTK_TREE_SELECT_MODE_EXTEND; - - _gtk_tree_selection_internal_select_node (priv->selection, - cursor_node, - cursor_tree, - cursor_path, - mode, - FALSE); - - /* We bail out if the original (tree, node) don't exist anymore after - * handling the selection-changed callback. We do return TRUE because - * the key press has been handled at this point. - */ - _gtk_tree_view_find_node (tree_view, cursor_path, &new_tree, &new_node); - - if (cursor_tree != new_tree || cursor_node != new_node) - return FALSE; - - gtk_tree_view_clamp_node_visible (tree_view, cursor_tree, cursor_node); - - gtk_widget_grab_focus (GTK_WIDGET (tree_view)); - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - - if (!priv->extend_selection_pressed) - gtk_tree_view_row_activated (tree_view, cursor_path, - priv->focus_column); - - gtk_tree_path_free (cursor_path); - - return TRUE; -} - -static gboolean -gtk_tree_view_real_toggle_cursor_row (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreeRBTree *new_tree = NULL; - GtkTreeRBNode *new_node = NULL; - GtkTreePath *cursor_path = NULL; - - if (!gtk_widget_has_focus (GTK_WIDGET (tree_view))) - return FALSE; - - if (priv->cursor_node == NULL) - return FALSE; - - cursor_path = _gtk_tree_path_new_from_rbtree (priv->cursor_tree, - priv->cursor_node); - - _gtk_tree_selection_internal_select_node (priv->selection, - priv->cursor_node, - priv->cursor_tree, - cursor_path, - GTK_TREE_SELECT_MODE_TOGGLE, - FALSE); - - /* We bail out if the original (tree, node) don't exist anymore after - * handling the selection-changed callback. We do return TRUE because - * the key press has been handled at this point. - */ - _gtk_tree_view_find_node (tree_view, cursor_path, &new_tree, &new_node); - - if (priv->cursor_node != new_node) - return FALSE; - - gtk_tree_view_clamp_node_visible (tree_view, - priv->cursor_tree, - priv->cursor_node); - - gtk_widget_grab_focus (GTK_WIDGET (tree_view)); - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - gtk_tree_path_free (cursor_path); - - return TRUE; -} - -static gboolean -gtk_tree_view_real_expand_collapse_cursor_row (GtkTreeView *tree_view, - gboolean logical, - gboolean expand, - gboolean open_all) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreePath *cursor_path = NULL; - - if (!gtk_widget_has_focus (GTK_WIDGET (tree_view))) - return FALSE; - - if (priv->cursor_node == NULL) - return FALSE; - - cursor_path = _gtk_tree_path_new_from_rbtree (priv->cursor_tree, - priv->cursor_node); - - /* Don't handle the event if we aren't an expander */ - if (!GTK_TREE_RBNODE_FLAG_SET (priv->cursor_node, GTK_TREE_RBNODE_IS_PARENT)) - return FALSE; - - if (!logical - && gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL) - expand = !expand; - - if (expand) - gtk_tree_view_real_expand_row (tree_view, - cursor_path, - priv->cursor_tree, - priv->cursor_node, - open_all); - else - gtk_tree_view_real_collapse_row (tree_view, - cursor_path, - priv->cursor_tree, - priv->cursor_node); - - gtk_tree_path_free (cursor_path); - - return TRUE; -} - -static gboolean -gtk_tree_view_real_select_cursor_parent (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreePath *cursor_path = NULL; - - if (!gtk_widget_has_focus (GTK_WIDGET (tree_view))) - goto out; - - if (priv->cursor_node == NULL) - goto out; - - cursor_path = _gtk_tree_path_new_from_rbtree (priv->cursor_tree, - priv->cursor_node); - - if (priv->cursor_tree->parent_node) - { - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - - gtk_tree_path_up (cursor_path); - - gtk_tree_view_real_set_cursor (tree_view, cursor_path, CLEAR_AND_SELECT | CLAMP_NODE); - gtk_tree_path_free (cursor_path); - - gtk_widget_grab_focus (GTK_WIDGET (tree_view)); - - return TRUE; - } - - out: - - priv->search_entry_avoid_unhandled_binding = TRUE; - return FALSE; -} - -static gboolean -gtk_tree_view_search_entry_flush_timeout (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - gtk_tree_view_search_popover_hide (priv->search_popover, tree_view); - priv->typeselect_flush_timeout = 0; - - return FALSE; -} - -static void -gtk_tree_view_ensure_interactive_directory (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkEventController *controller; - GtkGesture *gesture; - - if (priv->search_custom_entry_set) - return; - - if (priv->search_popover) - return; - - priv->search_popover = gtk_popover_new (); - gtk_css_node_insert_after (gtk_widget_get_css_node (GTK_WIDGET (tree_view)), - gtk_widget_get_css_node (priv->search_popover), - priv->header_node); - gtk_widget_set_parent (priv->search_popover, GTK_WIDGET (tree_view)); - gtk_popover_set_autohide (GTK_POPOVER (priv->search_popover), FALSE); - - controller = gtk_event_controller_key_new (); - g_signal_connect (controller, "key-pressed", - G_CALLBACK (gtk_tree_view_search_key_pressed), - tree_view); - gtk_widget_add_controller (priv->search_popover, controller); - - gesture = gtk_gesture_click_new (); - g_signal_connect (gesture, "pressed", - G_CALLBACK (gtk_tree_view_search_pressed_cb), tree_view); - gtk_widget_add_controller (priv->search_popover, GTK_EVENT_CONTROLLER (gesture)); - - controller = gtk_event_controller_scroll_new (GTK_EVENT_CONTROLLER_SCROLL_VERTICAL); - g_signal_connect (controller, "scroll", - G_CALLBACK (gtk_tree_view_search_scroll_event), - tree_view); - gtk_widget_add_controller (priv->search_popover, controller); - - priv->search_entry = gtk_text_new (); - - controller = gtk_text_get_key_controller (GTK_TEXT (priv->search_entry)); - gtk_event_controller_set_propagation_limit (controller, GTK_LIMIT_NONE); - - g_signal_connect (priv->search_entry, "activate", - G_CALLBACK (gtk_tree_view_search_activate), tree_view); - g_signal_connect (priv->search_entry, "preedit-changed", - G_CALLBACK (gtk_tree_view_search_preedit_changed), tree_view); - g_signal_connect (priv->search_entry, "changed", - G_CALLBACK (gtk_tree_view_search_changed), tree_view); - - gtk_popover_set_child (GTK_POPOVER (priv->search_popover), priv->search_entry); - - gtk_widget_realize (priv->search_entry); -} - -/* Pops up the interactive search entry. If keybinding is TRUE then the user - * started this by typing the start_interactive_search keybinding. Otherwise, it came from - */ -static gboolean -gtk_tree_view_real_start_interactive_search (GtkTreeView *tree_view, - gboolean keybinding) -{ - /* We only start interactive search if we have focus or the columns - * have focus. If one of our children have focus, we don't want to - * start the search. - */ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GList *list; - gboolean found_focus = FALSE; - - if (!priv->enable_search && !keybinding) - return FALSE; - - if (priv->search_custom_entry_set) - return FALSE; - - if (priv->search_popover && - gtk_widget_get_visible (priv->search_popover)) - return TRUE; - - for (list = priv->columns; list; list = list->next) - { - GtkTreeViewColumn *column; - GtkWidget *button; - - column = list->data; - if (!gtk_tree_view_column_get_visible (column)) - continue; - - button = gtk_tree_view_column_get_button (column); - if (gtk_widget_has_focus (button)) - { - found_focus = TRUE; - break; - } - } - - if (gtk_widget_has_focus (GTK_WIDGET (tree_view))) - found_focus = TRUE; - - if (!found_focus) - return FALSE; - - if (priv->search_column < 0) - return FALSE; - - gtk_tree_view_ensure_interactive_directory (tree_view); - - if (keybinding) - gtk_editable_set_text (GTK_EDITABLE (priv->search_entry), ""); - - /* Grab focus without selecting all the text. */ - gtk_text_grab_focus_without_selecting (GTK_TEXT (priv->search_entry)); - - gtk_popover_popup (GTK_POPOVER (priv->search_popover)); - if (priv->search_entry_changed_id == 0) - { - priv->search_entry_changed_id = - g_signal_connect (priv->search_entry, "changed", - G_CALLBACK (gtk_tree_view_search_init), - tree_view); - } - - priv->typeselect_flush_timeout = - g_timeout_add (GTK_TREE_VIEW_SEARCH_DIALOG_TIMEOUT, - (GSourceFunc) gtk_tree_view_search_entry_flush_timeout, - tree_view); - gdk_source_set_static_name_by_id (priv->typeselect_flush_timeout, "[gtk] gtk_tree_view_search_entry_flush_timeout"); - - /* search first matching iter */ - gtk_tree_view_search_init (priv->search_entry, tree_view); - - return TRUE; -} - -static gboolean -gtk_tree_view_start_interactive_search (GtkTreeView *tree_view) -{ - return gtk_tree_view_real_start_interactive_search (tree_view, TRUE); -} - -/* Callbacks */ -static void -gtk_tree_view_adjustment_changed (GtkAdjustment *adjustment, - GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - if (gtk_widget_get_realized (GTK_WIDGET (tree_view))) - { - GtkAllocation allocation; - int dy; - - gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation); - dy = priv->dy - (int) gtk_adjustment_get_value (priv->vadjustment); - - if (dy != 0) - { - /* update our dy and top_row */ - priv->dy = (int) gtk_adjustment_get_value (priv->vadjustment); - - update_prelight (tree_view, - priv->event_last_x, - priv->event_last_y); - - if (!priv->in_top_row_to_dy) - gtk_tree_view_dy_to_top_row (tree_view); - - } - } - - gtk_widget_queue_allocate (GTK_WIDGET (tree_view)); -} - - - -/* Public methods - */ - -/** - * gtk_tree_view_new: - * - * Creates a new `GtkTreeView` widget. - * - * Returns: A newly created `GtkTreeView` widget. - **/ -GtkWidget * -gtk_tree_view_new (void) -{ - return g_object_new (GTK_TYPE_TREE_VIEW, NULL); -} - -/** - * gtk_tree_view_new_with_model: - * @model: the model. - * - * Creates a new `GtkTreeView` widget with the model initialized to @model. - * - * Returns: A newly created `GtkTreeView` widget. - **/ -GtkWidget * -gtk_tree_view_new_with_model (GtkTreeModel *model) -{ - return g_object_new (GTK_TYPE_TREE_VIEW, "model", model, NULL); -} - -/* Public Accessors - */ - -/** - * gtk_tree_view_get_model: - * @tree_view: a `GtkTreeView` - * - * Returns the model the `GtkTreeView` is based on. Returns %NULL if the - * model is unset. - * - * Returns: (transfer none) (nullable): A `GtkTreeModel` - **/ -GtkTreeModel * -gtk_tree_view_get_model (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL); - - return priv->model; -} - -/** - * gtk_tree_view_set_model: - * @tree_view: A `GtkTreeView`. - * @model: (nullable): The model. - * - * Sets the model for a `GtkTreeView`. If the @tree_view already has a model - * set, it will remove it before setting the new model. If @model is %NULL, - * then it will unset the old model. - **/ -void -gtk_tree_view_set_model (GtkTreeView *tree_view, - GtkTreeModel *model) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model)); - - if (model == priv->model) - return; - - if (priv->scroll_to_path) - { - gtk_tree_row_reference_free (priv->scroll_to_path); - priv->scroll_to_path = NULL; - } - - if (priv->rubber_band_status) - gtk_tree_view_stop_rubber_band (tree_view); - - if (priv->model) - { - GList *tmplist = priv->columns; - - gtk_tree_view_unref_and_check_selection_tree (tree_view, priv->tree); - gtk_tree_view_stop_editing (tree_view, TRUE); - - g_signal_handlers_disconnect_by_func (priv->model, - gtk_tree_view_row_changed, - tree_view); - g_signal_handlers_disconnect_by_func (priv->model, - gtk_tree_view_row_inserted, - tree_view); - g_signal_handlers_disconnect_by_func (priv->model, - gtk_tree_view_row_has_child_toggled, - tree_view); - g_signal_handlers_disconnect_by_func (priv->model, - gtk_tree_view_row_deleted, - tree_view); - g_signal_handlers_disconnect_by_func (priv->model, - gtk_tree_view_rows_reordered, - tree_view); - - for (; tmplist; tmplist = tmplist->next) - _gtk_tree_view_column_unset_model (tmplist->data, - priv->model); - - if (priv->tree) - gtk_tree_view_free_rbtree (tree_view); - - gtk_tree_row_reference_free (priv->drag_dest_row); - priv->drag_dest_row = NULL; - gtk_tree_row_reference_free (priv->anchor); - priv->anchor = NULL; - gtk_tree_row_reference_free (priv->top_row); - priv->top_row = NULL; - gtk_tree_row_reference_free (priv->scroll_to_path); - priv->scroll_to_path = NULL; - - priv->scroll_to_column = NULL; - - g_object_unref (priv->model); - - priv->search_column = -1; - priv->fixed_height_check = 0; - priv->fixed_height = -1; - priv->dy = priv->top_row_dy = 0; - } - - priv->model = model; - - if (priv->model) - { - int i; - GtkTreePath *path; - GtkTreeIter iter; - GtkTreeModelFlags flags; - - if (priv->search_column == -1) - { - for (i = 0; i < gtk_tree_model_get_n_columns (model); i++) - { - GType type = gtk_tree_model_get_column_type (model, i); - - if (g_value_type_transformable (type, G_TYPE_STRING)) - { - priv->search_column = i; - break; - } - } - } - - g_object_ref (priv->model); - g_signal_connect (priv->model, - "row-changed", - G_CALLBACK (gtk_tree_view_row_changed), - tree_view); - g_signal_connect (priv->model, - "row-inserted", - G_CALLBACK (gtk_tree_view_row_inserted), - tree_view); - g_signal_connect (priv->model, - "row-has-child-toggled", - G_CALLBACK (gtk_tree_view_row_has_child_toggled), - tree_view); - g_signal_connect (priv->model, - "row-deleted", - G_CALLBACK (gtk_tree_view_row_deleted), - tree_view); - g_signal_connect (priv->model, - "rows-reordered", - G_CALLBACK (gtk_tree_view_rows_reordered), - tree_view); - - flags = gtk_tree_model_get_flags (priv->model); - if ((flags & GTK_TREE_MODEL_LIST_ONLY) == GTK_TREE_MODEL_LIST_ONLY) - priv->is_list = TRUE; - else - priv->is_list = FALSE; - - path = gtk_tree_path_new_first (); - if (gtk_tree_model_get_iter (priv->model, &iter, path)) - { - priv->tree = gtk_tree_rbtree_new (); - gtk_tree_view_build_tree (tree_view, priv->tree, &iter, 1, FALSE); - } - gtk_tree_path_free (path); - - /* FIXME: do I need to do this? gtk_tree_view_create_buttons (tree_view); */ - install_presize_handler (tree_view); - } - - gtk_tree_view_real_set_cursor (tree_view, NULL, CURSOR_INVALID); - - g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_MODEL]); - - if (priv->selection) - _gtk_tree_selection_emit_changed (priv->selection); - - if (gtk_widget_get_realized (GTK_WIDGET (tree_view))) - gtk_widget_queue_resize (GTK_WIDGET (tree_view)); -} - -/** - * gtk_tree_view_get_selection: - * @tree_view: A `GtkTreeView`. - * - * Gets the `GtkTreeSelection` associated with @tree_view. - * - * Returns: (transfer none): A `GtkTreeSelection` object. - **/ -GtkTreeSelection * -gtk_tree_view_get_selection (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL); - - return priv->selection; -} - -static void -gtk_tree_view_do_set_hadjustment (GtkTreeView *tree_view, - GtkAdjustment *adjustment) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - if (adjustment && priv->hadjustment == adjustment) - return; - - if (priv->hadjustment != NULL) - { - g_signal_handlers_disconnect_by_func (priv->hadjustment, - gtk_tree_view_adjustment_changed, - tree_view); - g_object_unref (priv->hadjustment); - } - - if (adjustment == NULL) - adjustment = gtk_adjustment_new (0.0, 0.0, 0.0, - 0.0, 0.0, 0.0); - - g_signal_connect (adjustment, "value-changed", - G_CALLBACK (gtk_tree_view_adjustment_changed), tree_view); - priv->hadjustment = g_object_ref_sink (adjustment); - /* FIXME: Adjustment should probably be populated here with fresh values, but - * internal details are too complicated for me to decipher right now. - */ - gtk_tree_view_adjustment_changed (NULL, tree_view); - - g_object_notify (G_OBJECT (tree_view), "hadjustment"); -} - -static void -gtk_tree_view_do_set_vadjustment (GtkTreeView *tree_view, - GtkAdjustment *adjustment) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - if (adjustment && priv->vadjustment == adjustment) - return; - - if (priv->vadjustment != NULL) - { - g_signal_handlers_disconnect_by_func (priv->vadjustment, - gtk_tree_view_adjustment_changed, - tree_view); - g_object_unref (priv->vadjustment); - } - - if (adjustment == NULL) - adjustment = gtk_adjustment_new (0.0, 0.0, 0.0, - 0.0, 0.0, 0.0); - - g_signal_connect (adjustment, "value-changed", - G_CALLBACK (gtk_tree_view_adjustment_changed), tree_view); - priv->vadjustment = g_object_ref_sink (adjustment); - /* FIXME: Adjustment should probably be populated here with fresh values, but - * internal details are too complicated for me to decipher right now. - */ - gtk_tree_view_adjustment_changed (NULL, tree_view); - g_object_notify (G_OBJECT (tree_view), "vadjustment"); -} - -/* Column and header operations */ - -/** - * gtk_tree_view_get_headers_visible: - * @tree_view: A `GtkTreeView`. - * - * Returns %TRUE if the headers on the @tree_view are visible. - * - * Returns: Whether the headers are visible or not. - **/ -gboolean -gtk_tree_view_get_headers_visible (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); - - return priv->headers_visible; -} - -/** - * gtk_tree_view_set_headers_visible: - * @tree_view: A `GtkTreeView`. - * @headers_visible: %TRUE if the headers are visible - * - * Sets the visibility state of the headers. - **/ -void -gtk_tree_view_set_headers_visible (GtkTreeView *tree_view, - gboolean headers_visible) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GList *list; - GtkTreeViewColumn *column; - GtkWidget *button; - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - - headers_visible = !! headers_visible; - - if (priv->headers_visible == headers_visible) - return; - - priv->headers_visible = headers_visible == TRUE; - - if (gtk_widget_get_realized (GTK_WIDGET (tree_view))) - { - if (headers_visible) - { - if (gtk_widget_get_mapped (GTK_WIDGET (tree_view))) - gtk_tree_view_map_buttons (tree_view); - } - else - { - - for (list = priv->columns; list; list = list->next) - { - column = list->data; - button = gtk_tree_view_column_get_button (column); - - gtk_widget_hide (button); - gtk_widget_unmap (button); - } - } - } - - gtk_widget_queue_resize (GTK_WIDGET (tree_view)); - - g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_HEADERS_VISIBLE]); -} - -/** - * gtk_tree_view_columns_autosize: - * @tree_view: A `GtkTreeView`. - * - * Resizes all columns to their optimal width. Only works after the - * treeview has been realized. - **/ -void -gtk_tree_view_columns_autosize (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - gboolean dirty = FALSE; - GList *list; - GtkTreeViewColumn *column; - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - - for (list = priv->columns; list; list = list->next) - { - column = list->data; - if (gtk_tree_view_column_get_sizing (column) == GTK_TREE_VIEW_COLUMN_AUTOSIZE) - continue; - _gtk_tree_view_column_cell_set_dirty (column, TRUE); - dirty = TRUE; - } - - if (dirty) - gtk_widget_queue_resize (GTK_WIDGET (tree_view)); -} - -/** - * gtk_tree_view_set_headers_clickable: - * @tree_view: A `GtkTreeView`. - * @setting: %TRUE if the columns are clickable. - * - * Allow the column title buttons to be clicked. - **/ -void -gtk_tree_view_set_headers_clickable (GtkTreeView *tree_view, - gboolean setting) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GList *list; - gboolean changed = FALSE; - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - - for (list = priv->columns; list; list = list->next) - { - if (gtk_tree_view_column_get_clickable (GTK_TREE_VIEW_COLUMN (list->data)) != setting) - { - gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (list->data), setting); - changed = TRUE; - } - } - - if (changed) - g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_HEADERS_CLICKABLE]); -} - - -/** - * gtk_tree_view_get_headers_clickable: - * @tree_view: A `GtkTreeView`. - * - * Returns whether all header columns are clickable. - * - * Returns: %TRUE if all header columns are clickable, otherwise %FALSE - **/ -gboolean -gtk_tree_view_get_headers_clickable (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GList *list; - - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); - - for (list = priv->columns; list; list = list->next) - if (!gtk_tree_view_column_get_clickable (GTK_TREE_VIEW_COLUMN (list->data))) - return FALSE; - - return TRUE; -} - -/** - * gtk_tree_view_set_activate_on_single_click: - * @tree_view: a `GtkTreeView` - * @single: %TRUE to emit row-activated on a single click - * - * Cause the `GtkTreeView`::row-activated signal to be emitted - * on a single click instead of a double click. - **/ -void -gtk_tree_view_set_activate_on_single_click (GtkTreeView *tree_view, - gboolean single) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - - single = single != FALSE; - - if (priv->activate_on_single_click == single) - return; - - priv->activate_on_single_click = single; - g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_ACTIVATE_ON_SINGLE_CLICK]); -} - -/** - * gtk_tree_view_get_activate_on_single_click: - * @tree_view: a `GtkTreeView` - * - * Gets the setting set by gtk_tree_view_set_activate_on_single_click(). - * - * Returns: %TRUE if row-activated will be emitted on a single click - **/ -gboolean -gtk_tree_view_get_activate_on_single_click (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); - - return priv->activate_on_single_click; -} - -/* Public Column functions - */ - -/** - * gtk_tree_view_append_column: - * @tree_view: A `GtkTreeView`. - * @column: The `GtkTreeViewColumn` to add. - * - * Appends @column to the list of columns. If @tree_view has “fixed_height” - * mode enabled, then @column must have its “sizing” property set to be - * GTK_TREE_VIEW_COLUMN_FIXED. - * - * Returns: The number of columns in @tree_view after appending. - **/ -int -gtk_tree_view_append_column (GtkTreeView *tree_view, - GtkTreeViewColumn *column) -{ - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1); - g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (column), -1); - g_return_val_if_fail (gtk_tree_view_column_get_tree_view (column) == NULL, -1); - - return gtk_tree_view_insert_column (tree_view, column, -1); -} - -/** - * gtk_tree_view_remove_column: - * @tree_view: A `GtkTreeView`. - * @column: The `GtkTreeViewColumn` to remove. - * - * Removes @column from @tree_view. - * - * Returns: The number of columns in @tree_view after removing. - **/ -int -gtk_tree_view_remove_column (GtkTreeView *tree_view, - GtkTreeViewColumn *column) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1); - g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (column), -1); - g_return_val_if_fail (gtk_tree_view_column_get_tree_view (column) == GTK_WIDGET (tree_view), -1); - - if (priv->focus_column == column) - _gtk_tree_view_set_focus_column (tree_view, NULL); - - if (priv->edited_column == column) - { - gtk_tree_view_stop_editing (tree_view, TRUE); - - /* no need to, but just to be sure ... */ - priv->edited_column = NULL; - } - - if (priv->expander_column == column) - priv->expander_column = NULL; - - g_signal_handlers_disconnect_by_func (column, - G_CALLBACK (column_sizing_notify), - tree_view); - - _gtk_tree_view_column_unset_tree_view (column); - - priv->columns = g_list_remove (priv->columns, column); - priv->n_columns--; - - if (gtk_widget_get_realized (GTK_WIDGET (tree_view))) - { - GList *list; - - for (list = priv->columns; list; list = list->next) - { - GtkTreeViewColumn *tmp_column; - - tmp_column = GTK_TREE_VIEW_COLUMN (list->data); - if (gtk_tree_view_column_get_visible (tmp_column)) - _gtk_tree_view_column_cell_set_dirty (tmp_column, TRUE); - } - - gtk_widget_queue_resize (GTK_WIDGET (tree_view)); - } - - g_object_unref (column); - g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0); - - return priv->n_columns; -} - -/** - * gtk_tree_view_insert_column: - * @tree_view: A `GtkTreeView`. - * @column: The `GtkTreeViewColumn` to be inserted. - * @position: The position to insert @column in. - * - * This inserts the @column into the @tree_view at @position. If @position is - * -1, then the column is inserted at the end. If @tree_view has - * “fixed_height” mode enabled, then @column must have its “sizing” property - * set to be GTK_TREE_VIEW_COLUMN_FIXED. - * - * Returns: The number of columns in @tree_view after insertion. - **/ -int -gtk_tree_view_insert_column (GtkTreeView *tree_view, - GtkTreeViewColumn *column, - int position) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1); - g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (column), -1); - g_return_val_if_fail (gtk_tree_view_column_get_tree_view (column) == NULL, -1); - - if (priv->fixed_height_mode) - g_return_val_if_fail (gtk_tree_view_column_get_sizing (column) - == GTK_TREE_VIEW_COLUMN_FIXED, -1); - - if (position < 0 || position > priv->n_columns) - position = priv->n_columns; - - g_object_ref_sink (column); - - g_signal_connect (column, "notify::sizing", - G_CALLBACK (column_sizing_notify), tree_view); - - priv->columns = g_list_insert (priv->columns, - column, position); - priv->n_columns++; - - _gtk_tree_view_column_set_tree_view (column, tree_view); - - /* XXX: We need to reparent the node into the header, somebody make that a real widget */ - gtk_css_node_set_parent (gtk_widget_get_css_node (gtk_tree_view_column_get_button (column)), NULL); - gtk_tree_view_update_button_position (tree_view, column); - - if (gtk_widget_get_realized (GTK_WIDGET (tree_view))) - { - GList *list; - - _gtk_tree_view_column_realize_button (column); - - for (list = priv->columns; list; list = list->next) - { - column = GTK_TREE_VIEW_COLUMN (list->data); - if (gtk_tree_view_column_get_visible (column)) - _gtk_tree_view_column_cell_set_dirty (column, TRUE); - } - gtk_widget_queue_resize (GTK_WIDGET (tree_view)); - } - - g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0); - - return priv->n_columns; -} - -/** - * gtk_tree_view_insert_column_with_attributes: - * @tree_view: A `GtkTreeView` - * @position: The position to insert the new column in - * @title: The title to set the header to - * @cell: The `GtkCellRenderer` - * @...: A %NULL-terminated list of attributes - * - * Creates a new `GtkTreeViewColumn` and inserts it into the @tree_view at - * @position. If @position is -1, then the newly created column is inserted at - * the end. The column is initialized with the attributes given. If @tree_view - * has “fixed_height” mode enabled, then the new column will have its sizing - * property set to be GTK_TREE_VIEW_COLUMN_FIXED. - * - * Returns: The number of columns in @tree_view after insertion. - **/ -int -gtk_tree_view_insert_column_with_attributes (GtkTreeView *tree_view, - int position, - const char *title, - GtkCellRenderer *cell, - ...) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreeViewColumn *column; - char *attribute; - va_list args; - int column_id; - - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1); - - column = gtk_tree_view_column_new (); - if (priv->fixed_height_mode) - gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED); - - gtk_tree_view_column_set_title (column, title); - gtk_tree_view_column_pack_start (column, cell, TRUE); - - va_start (args, cell); - - attribute = va_arg (args, char *); - - while (attribute != NULL) - { - column_id = va_arg (args, int); - gtk_tree_view_column_add_attribute (column, cell, attribute, column_id); - attribute = va_arg (args, char *); - } - - va_end (args); - - return gtk_tree_view_insert_column (tree_view, column, position); -} - -/** - * gtk_tree_view_insert_column_with_data_func: - * @tree_view: a `GtkTreeView` - * @position: Position to insert, -1 for append - * @title: column title - * @cell: cell renderer for column - * @func: function to set attributes of cell renderer - * @data: data for @func - * @dnotify: destroy notifier for @data - * - * Convenience function that inserts a new column into the `GtkTreeView` - * with the given cell renderer and a `GtkTreeCellDataFunc` to set cell renderer - * attributes (normally using data from the model). See also - * gtk_tree_view_column_set_cell_data_func(), gtk_tree_view_column_pack_start(). - * If @tree_view has “fixed_height” mode enabled, then the new column will have its - * “sizing” property set to be GTK_TREE_VIEW_COLUMN_FIXED. - * - * Returns: number of columns in the tree view post-insert - **/ -int -gtk_tree_view_insert_column_with_data_func (GtkTreeView *tree_view, - int position, - const char *title, - GtkCellRenderer *cell, - GtkTreeCellDataFunc func, - gpointer data, - GDestroyNotify dnotify) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreeViewColumn *column; - - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1); - - column = gtk_tree_view_column_new (); - if (priv->fixed_height_mode) - gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED); - - gtk_tree_view_column_set_title (column, title); - gtk_tree_view_column_pack_start (column, cell, TRUE); - gtk_tree_view_column_set_cell_data_func (column, cell, func, data, dnotify); - - return gtk_tree_view_insert_column (tree_view, column, position); -} - -/** - * gtk_tree_view_get_n_columns: - * @tree_view: a `GtkTreeView` - * - * Queries the number of columns in the given @tree_view. - * - * Returns: The number of columns in the @tree_view - **/ -guint -gtk_tree_view_get_n_columns (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), 0); - - return priv->n_columns; -} - -/** - * gtk_tree_view_get_column: - * @tree_view: A `GtkTreeView`. - * @n: The position of the column, counting from 0. - * - * Gets the `GtkTreeViewColumn` at the given position in the #tree_view. - * - * Returns: (nullable) (transfer none): The `GtkTreeViewColumn`, or %NULL if the - * position is outside the range of columns. - **/ -GtkTreeViewColumn * -gtk_tree_view_get_column (GtkTreeView *tree_view, - int n) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL); - - if (n < 0 || n >= priv->n_columns) - return NULL; - - if (priv->columns == NULL) - return NULL; - - return GTK_TREE_VIEW_COLUMN (g_list_nth (priv->columns, n)->data); -} - -/** - * gtk_tree_view_get_columns: - * @tree_view: A `GtkTreeView` - * - * Returns a `GList` of all the `GtkTreeViewColumn`s currently in @tree_view. - * The returned list must be freed with g_list_free (). - * - * Returns: (element-type GtkTreeViewColumn) (transfer container): A list of `GtkTreeViewColumn`s - **/ -GList * -gtk_tree_view_get_columns (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL); - - return g_list_copy (priv->columns); -} - -/** - * gtk_tree_view_move_column_after: - * @tree_view: A `GtkTreeView` - * @column: The `GtkTreeViewColumn` to be moved. - * @base_column: (nullable): The `GtkTreeViewColumn` to be moved relative to - * - * Moves @column to be after to @base_column. If @base_column is %NULL, then - * @column is placed in the first position. - **/ -void -gtk_tree_view_move_column_after (GtkTreeView *tree_view, - GtkTreeViewColumn *column, - GtkTreeViewColumn *base_column) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GList *column_list_el, *base_el = NULL; - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - - column_list_el = g_list_find (priv->columns, column); - g_return_if_fail (column_list_el != NULL); - - if (base_column) - { - base_el = g_list_find (priv->columns, base_column); - g_return_if_fail (base_el != NULL); - } - - if (column_list_el->prev == base_el) - return; - - priv->columns = g_list_remove_link (priv->columns, column_list_el); - if (base_el == NULL) - { - column_list_el->prev = NULL; - column_list_el->next = priv->columns; - if (column_list_el->next) - column_list_el->next->prev = column_list_el; - priv->columns = column_list_el; - } - else - { - column_list_el->prev = base_el; - column_list_el->next = base_el->next; - if (column_list_el->next) - column_list_el->next->prev = column_list_el; - base_el->next = column_list_el; - } - - gtk_tree_view_update_button_position (tree_view, column); - - gtk_widget_queue_resize (GTK_WIDGET (tree_view)); - - g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0); -} - -/** - * gtk_tree_view_set_expander_column: - * @tree_view: A `GtkTreeView` - * @column: (nullable): %NULL, or the column to draw the expander arrow at. - * - * Sets the column to draw the expander arrow at. It must be in @tree_view. - * If @column is %NULL, then the expander arrow is always at the first - * visible column. - * - * If you do not want expander arrow to appear in your tree, set the - * expander column to a hidden column. - **/ -void -gtk_tree_view_set_expander_column (GtkTreeView *tree_view, - GtkTreeViewColumn *column) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - g_return_if_fail (column == NULL || GTK_IS_TREE_VIEW_COLUMN (column)); - g_return_if_fail (column == NULL || gtk_tree_view_column_get_tree_view (column) == GTK_WIDGET (tree_view)); - - if (priv->expander_column != column) - { - priv->expander_column = column; - g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_EXPANDER_COLUMN]); - } -} - -/** - * gtk_tree_view_get_expander_column: - * @tree_view: A `GtkTreeView` - * - * Returns the column that is the current expander column, - * or %NULL if none has been set. - * This column has the expander arrow drawn next to it. - * - * Returns: (transfer none) (nullable): The expander column. - **/ -GtkTreeViewColumn * -gtk_tree_view_get_expander_column (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GList *list; - - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL); - - for (list = priv->columns; list; list = list->next) - if (gtk_tree_view_is_expander_column (tree_view, GTK_TREE_VIEW_COLUMN (list->data))) - return (GtkTreeViewColumn *) list->data; - return NULL; -} - - -/** - * gtk_tree_view_set_column_drag_function: - * @tree_view: A `GtkTreeView`. - * @func: (nullable): A function to determine which columns are reorderable - * @user_data: (closure): User data to be passed to @func - * @destroy: (nullable): Destroy notifier for @user_data - * - * Sets a user function for determining where a column may be dropped when - * dragged. This function is called on every column pair in turn at the - * beginning of a column drag to determine where a drop can take place. The - * arguments passed to @func are: the @tree_view, the `GtkTreeViewColumn` being - * dragged, the two `GtkTreeViewColumn`s determining the drop spot, and - * @user_data. If either of the `GtkTreeViewColumn` arguments for the drop spot - * are %NULL, then they indicate an edge. If @func is set to be %NULL, then - * @tree_view reverts to the default behavior of allowing all columns to be - * dropped everywhere. - **/ -void -gtk_tree_view_set_column_drag_function (GtkTreeView *tree_view, - GtkTreeViewColumnDropFunc func, - gpointer user_data, - GDestroyNotify destroy) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - - if (priv->column_drop_func_data_destroy) - priv->column_drop_func_data_destroy (priv->column_drop_func_data); - - priv->column_drop_func = func; - priv->column_drop_func_data = user_data; - priv->column_drop_func_data_destroy = destroy; -} - -/** - * gtk_tree_view_scroll_to_point: - * @tree_view: a `GtkTreeView` - * @tree_x: X coordinate of new top-left pixel of visible area, or -1 - * @tree_y: Y coordinate of new top-left pixel of visible area, or -1 - * - * Scrolls the tree view such that the top-left corner of the visible - * area is @tree_x, @tree_y, where @tree_x and @tree_y are specified - * in tree coordinates. The @tree_view must be realized before - * this function is called. If it isn't, you probably want to be - * using gtk_tree_view_scroll_to_cell(). - * - * If either @tree_x or @tree_y are -1, then that direction isn’t scrolled. - **/ -void -gtk_tree_view_scroll_to_point (GtkTreeView *tree_view, - int tree_x, - int tree_y) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkAdjustment *hadj; - GtkAdjustment *vadj; - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view))); - - hadj = priv->hadjustment; - vadj = priv->vadjustment; - - if (tree_x != -1) - gtk_adjustment_animate_to_value (hadj, tree_x); - if (tree_y != -1) - gtk_adjustment_animate_to_value (vadj, tree_y); -} - -/** - * gtk_tree_view_scroll_to_cell: - * @tree_view: A `GtkTreeView`. - * @path: (nullable): The path of the row to move to - * @column: (nullable): The `GtkTreeViewColumn` to move horizontally to - * @use_align: whether to use alignment arguments, or %FALSE. - * @row_align: The vertical alignment of the row specified by @path. - * @col_align: The horizontal alignment of the column specified by @column. - * - * Moves the alignments of @tree_view to the position specified by @column and - * @path. If @column is %NULL, then no horizontal scrolling occurs. Likewise, - * if @path is %NULL no vertical scrolling occurs. At a minimum, one of @column - * or @path need to be non-%NULL. @row_align determines where the row is - * placed, and @col_align determines where @column is placed. Both are expected - * to be between 0.0 and 1.0. 0.0 means left/top alignment, 1.0 means - * right/bottom alignment, 0.5 means center. - * - * If @use_align is %FALSE, then the alignment arguments are ignored, and the - * tree does the minimum amount of work to scroll the cell onto the screen. - * This means that the cell will be scrolled to the edge closest to its current - * position. If the cell is currently visible on the screen, nothing is done. - * - * This function only works if the model is set, and @path is a valid row on the - * model. If the model changes before the @tree_view is realized, the centered - * path will be modified to reflect this change. - **/ -void -gtk_tree_view_scroll_to_cell (GtkTreeView *tree_view, - GtkTreePath *path, - GtkTreeViewColumn *column, - gboolean use_align, - float row_align, - float col_align) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - g_return_if_fail (priv->model != NULL); - g_return_if_fail (priv->tree != NULL); - g_return_if_fail (row_align >= 0.0 && row_align <= 1.0); - g_return_if_fail (col_align >= 0.0 && col_align <= 1.0); - g_return_if_fail (path != NULL || column != NULL); - - row_align = CLAMP (row_align, 0.0, 1.0); - col_align = CLAMP (col_align, 0.0, 1.0); - - - /* Note: Despite the benefits that come from having one code path for the - * scrolling code, we short-circuit validate_visible_area's immplementation as - * it is much slower than just going to the point. - */ - if (!gtk_widget_get_visible (GTK_WIDGET (tree_view)) || - !gtk_widget_get_realized (GTK_WIDGET (tree_view)) || - _gtk_widget_get_alloc_needed (GTK_WIDGET (tree_view)) || - GTK_TREE_RBNODE_FLAG_SET (priv->tree->root, GTK_TREE_RBNODE_DESCENDANTS_INVALID)) - { - if (priv->scroll_to_path) - gtk_tree_row_reference_free (priv->scroll_to_path); - - priv->scroll_to_path = NULL; - priv->scroll_to_column = NULL; - - if (path) - priv->scroll_to_path = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), priv->model, path); - if (column) - priv->scroll_to_column = column; - priv->scroll_to_use_align = use_align; - priv->scroll_to_row_align = row_align; - priv->scroll_to_col_align = col_align; - - install_presize_handler (tree_view); - } - else - { - GdkRectangle cell_rect; - GdkRectangle vis_rect; - int dest_x, dest_y; - - gtk_tree_view_get_background_area (tree_view, path, column, &cell_rect); - gtk_tree_view_get_visible_rect (tree_view, &vis_rect); - - cell_rect.y = TREE_WINDOW_Y_TO_RBTREE_Y (priv, cell_rect.y); - - dest_x = vis_rect.x; - dest_y = vis_rect.y; - - if (column) - { - if (use_align) - { - dest_x = cell_rect.x - ((vis_rect.width - cell_rect.width) * col_align); - } - else - { - if (cell_rect.x < vis_rect.x) - dest_x = cell_rect.x; - if (cell_rect.x + cell_rect.width > vis_rect.x + vis_rect.width) - dest_x = cell_rect.x + cell_rect.width - vis_rect.width; - } - } - - if (path) - { - if (use_align) - { - dest_y = cell_rect.y - ((vis_rect.height - cell_rect.height) * row_align); - dest_y = MAX (dest_y, 0); - } - else - { - if (cell_rect.y < vis_rect.y) - dest_y = cell_rect.y; - if (cell_rect.y + cell_rect.height > vis_rect.y + vis_rect.height) - dest_y = cell_rect.y + cell_rect.height - vis_rect.height; - } - } - - gtk_tree_view_scroll_to_point (tree_view, dest_x, dest_y); - } -} - -/** - * gtk_tree_view_row_activated: - * @tree_view: A `GtkTreeView` - * @path: The `GtkTreePath` to be activated. - * @column: (nullable): The `GtkTreeViewColumn` to be activated. - * - * Activates the cell determined by @path and @column. - **/ -void -gtk_tree_view_row_activated (GtkTreeView *tree_view, - GtkTreePath *path, - GtkTreeViewColumn *column) -{ - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - - g_signal_emit (tree_view, tree_view_signals[ROW_ACTIVATED], 0, path, column); -} - - -static void -gtk_tree_view_expand_all_emission_helper (GtkTreeRBTree *tree, - GtkTreeRBNode *node, - gpointer data) -{ - GtkTreeView *tree_view = data; - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - if ((node->flags & GTK_TREE_RBNODE_IS_PARENT) == GTK_TREE_RBNODE_IS_PARENT && - node->children) - { - GtkTreePath *path; - GtkTreeIter iter; - - path = _gtk_tree_path_new_from_rbtree (tree, node); - gtk_tree_model_get_iter (priv->model, &iter, path); - - g_signal_emit (tree_view, tree_view_signals[ROW_EXPANDED], 0, &iter, path); - - gtk_tree_path_free (path); - } - - if (node->children) - gtk_tree_rbtree_traverse (node->children, - node->children->root, - G_PRE_ORDER, - gtk_tree_view_expand_all_emission_helper, - tree_view); -} - -/** - * gtk_tree_view_expand_all: - * @tree_view: A `GtkTreeView`. - * - * Recursively expands all nodes in the @tree_view. - **/ -void -gtk_tree_view_expand_all (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreePath *path; - GtkTreeRBTree *tree; - GtkTreeRBNode *node; - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - - if (priv->tree == NULL) - return; - - path = gtk_tree_path_new_first (); - _gtk_tree_view_find_node (tree_view, path, &tree, &node); - - while (node) - { - gtk_tree_view_real_expand_row (tree_view, path, tree, node, TRUE); - node = gtk_tree_rbtree_next (tree, node); - gtk_tree_path_next (path); - } - - gtk_tree_path_free (path); -} - -/** - * gtk_tree_view_collapse_all: - * @tree_view: A `GtkTreeView`. - * - * Recursively collapses all visible, expanded nodes in @tree_view. - **/ -void -gtk_tree_view_collapse_all (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreeRBTree *tree; - GtkTreeRBNode *node; - GtkTreePath *path; - int *indices; - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - - if (priv->tree == NULL) - return; - - path = gtk_tree_path_new (); - gtk_tree_path_down (path); - indices = gtk_tree_path_get_indices (path); - - tree = priv->tree; - node = gtk_tree_rbtree_first (tree); - - while (node) - { - if (node->children) - gtk_tree_view_real_collapse_row (tree_view, path, tree, node); - indices[0]++; - node = gtk_tree_rbtree_next (tree, node); - } - - gtk_tree_path_free (path); -} - -/** - * gtk_tree_view_expand_to_path: - * @tree_view: A `GtkTreeView`. - * @path: path to a row. - * - * Expands the row at @path. This will also expand all parent rows of - * @path as necessary. - **/ -void -gtk_tree_view_expand_to_path (GtkTreeView *tree_view, - GtkTreePath *path) -{ - int i, depth; - int *indices; - GtkTreePath *tmp; - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - g_return_if_fail (path != NULL); - - depth = gtk_tree_path_get_depth (path); - indices = gtk_tree_path_get_indices (path); - - tmp = gtk_tree_path_new (); - g_return_if_fail (tmp != NULL); - - for (i = 0; i < depth; i++) - { - gtk_tree_path_append_index (tmp, indices[i]); - gtk_tree_view_expand_row (tree_view, tmp, FALSE); - } - - gtk_tree_path_free (tmp); -} - -/* FIXME the bool return values for expand_row and collapse_row are - * not analogous; they should be TRUE if the row had children and - * was not already in the requested state. - */ - - -static gboolean -gtk_tree_view_real_expand_row (GtkTreeView *tree_view, - GtkTreePath *path, - GtkTreeRBTree *tree, - GtkTreeRBNode *node, - gboolean open_all) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreeIter iter; - GtkTreeIter temp; - gboolean expand; - - remove_auto_expand_timeout (tree_view); - - if (node->children && !open_all) - return FALSE; - - if (! GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_PARENT)) - return FALSE; - - gtk_tree_model_get_iter (priv->model, &iter, path); - if (! gtk_tree_model_iter_has_child (priv->model, &iter)) - return FALSE; - - - if (node->children && open_all) - { - gboolean retval = FALSE; - GtkTreePath *tmp_path = gtk_tree_path_copy (path); - - gtk_tree_path_append_index (tmp_path, 0); - tree = node->children; - node = gtk_tree_rbtree_first (tree); - /* try to expand the children */ - do - { - gboolean t; - t = gtk_tree_view_real_expand_row (tree_view, tmp_path, tree, node, - TRUE); - if (t) - retval = TRUE; - - gtk_tree_path_next (tmp_path); - node = gtk_tree_rbtree_next (tree, node); - } - while (node != NULL); - - gtk_tree_path_free (tmp_path); - - return retval; - } - - g_signal_emit (tree_view, tree_view_signals[TEST_EXPAND_ROW], 0, &iter, path, &expand); - - if (!gtk_tree_model_iter_has_child (priv->model, &iter)) - return FALSE; - - if (expand) - return FALSE; - - node->children = gtk_tree_rbtree_new (); - node->children->parent_tree = tree; - node->children->parent_node = node; - - gtk_tree_model_iter_children (priv->model, &temp, &iter); - - gtk_tree_view_build_tree (tree_view, - node->children, - &temp, - gtk_tree_path_get_depth (path) + 1, - open_all); - - install_presize_handler (tree_view); - - g_signal_emit (tree_view, tree_view_signals[ROW_EXPANDED], 0, &iter, path); - if (open_all && node->children) - { - gtk_tree_rbtree_traverse (node->children, - node->children->root, - G_PRE_ORDER, - gtk_tree_view_expand_all_emission_helper, - tree_view); - } - return TRUE; -} - - -/** - * gtk_tree_view_expand_row: - * @tree_view: a `GtkTreeView` - * @path: path to a row - * @open_all: whether to recursively expand, or just expand immediate children - * - * Opens the row so its children are visible. - * - * Returns: %TRUE if the row existed and had children - **/ -gboolean -gtk_tree_view_expand_row (GtkTreeView *tree_view, - GtkTreePath *path, - gboolean open_all) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreeRBTree *tree; - GtkTreeRBNode *node; - - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); - g_return_val_if_fail (priv->model != NULL, FALSE); - g_return_val_if_fail (path != NULL, FALSE); - - if (_gtk_tree_view_find_node (tree_view, - path, - &tree, - &node)) - return FALSE; - - if (tree != NULL) - return gtk_tree_view_real_expand_row (tree_view, path, tree, node, open_all); - else - return FALSE; -} - -static gboolean -gtk_tree_view_real_collapse_row (GtkTreeView *tree_view, - GtkTreePath *path, - GtkTreeRBTree *tree, - GtkTreeRBNode *node) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreeIter iter; - GtkTreeIter children; - gboolean collapse; - GList *list; - gboolean selection_changed, cursor_changed; - - remove_auto_expand_timeout (tree_view); - - if (node->children == NULL) - return FALSE; - gtk_tree_model_get_iter (priv->model, &iter, path); - - g_signal_emit (tree_view, tree_view_signals[TEST_COLLAPSE_ROW], 0, &iter, path, &collapse); - - if (collapse) - return FALSE; - - /* if the prelighted node is a child of us, we want to unprelight it. We have - * a chance to prelight the correct node below */ - - if (priv->prelight_tree) - { - GtkTreeRBTree *parent_tree; - GtkTreeRBNode *parent_node; - - parent_tree = priv->prelight_tree->parent_tree; - parent_node = priv->prelight_tree->parent_node; - while (parent_tree) - { - if (parent_tree == tree && parent_node == node) - { - ensure_unprelighted (tree_view); - break; - } - parent_node = parent_tree->parent_node; - parent_tree = parent_tree->parent_tree; - } - } - - TREE_VIEW_INTERNAL_ASSERT (gtk_tree_model_iter_children (priv->model, &children, &iter), FALSE); - - for (list = priv->columns; list; list = list->next) - { - GtkTreeViewColumn *column = list->data; - - if (gtk_tree_view_column_get_visible (column) == FALSE) - continue; - if (gtk_tree_view_column_get_sizing (column) == GTK_TREE_VIEW_COLUMN_AUTOSIZE) - _gtk_tree_view_column_cell_set_dirty (column, TRUE); - } - - if (priv->cursor_node) - { - cursor_changed = (node->children == priv->cursor_tree) - || gtk_tree_rbtree_contains (node->children, priv->cursor_tree); - } - else - cursor_changed = FALSE; - - if (gtk_tree_row_reference_valid (priv->anchor)) - { - GtkTreePath *anchor_path = gtk_tree_row_reference_get_path (priv->anchor); - if (gtk_tree_path_is_ancestor (path, anchor_path)) - { - gtk_tree_row_reference_free (priv->anchor); - priv->anchor = NULL; - } - gtk_tree_path_free (anchor_path); - } - - selection_changed = gtk_tree_view_unref_and_check_selection_tree (tree_view, node->children); - - /* Stop a pending double click */ - gtk_event_controller_reset (GTK_EVENT_CONTROLLER (priv->click_gesture)); - - gtk_tree_rbtree_remove (node->children); - - if (cursor_changed) - gtk_tree_view_real_set_cursor (tree_view, path, CLEAR_AND_SELECT | CURSOR_INVALID); - if (selection_changed) - g_signal_emit_by_name (priv->selection, "changed"); - - if (gtk_widget_get_mapped (GTK_WIDGET (tree_view))) - { - gtk_widget_queue_resize (GTK_WIDGET (tree_view)); - } - - g_signal_emit (tree_view, tree_view_signals[ROW_COLLAPSED], 0, &iter, path); - - if (gtk_widget_get_mapped (GTK_WIDGET (tree_view))) - update_prelight (tree_view, - priv->event_last_x, - priv->event_last_y); - - return TRUE; -} - -/** - * gtk_tree_view_collapse_row: - * @tree_view: a `GtkTreeView` - * @path: path to a row in the @tree_view - * - * Collapses a row (hides its child rows, if they exist). - * - * Returns: %TRUE if the row was collapsed. - **/ -gboolean -gtk_tree_view_collapse_row (GtkTreeView *tree_view, - GtkTreePath *path) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreeRBTree *tree; - GtkTreeRBNode *node; - - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); - g_return_val_if_fail (priv->tree != NULL, FALSE); - g_return_val_if_fail (path != NULL, FALSE); - - if (_gtk_tree_view_find_node (tree_view, - path, - &tree, - &node)) - return FALSE; - - if (tree == NULL || node->children == NULL) - return FALSE; - - return gtk_tree_view_real_collapse_row (tree_view, path, tree, node); -} - -static void -gtk_tree_view_map_expanded_rows_helper (GtkTreeView *tree_view, - GtkTreeRBTree *tree, - GtkTreePath *path, - GtkTreeViewMappingFunc func, - gpointer user_data) -{ - GtkTreeRBNode *node; - - if (tree == NULL || tree->root == NULL) - return; - - node = gtk_tree_rbtree_first (tree); - - while (node) - { - if (node->children) - { - (* func) (tree_view, path, user_data); - gtk_tree_path_down (path); - gtk_tree_view_map_expanded_rows_helper (tree_view, node->children, path, func, user_data); - gtk_tree_path_up (path); - } - gtk_tree_path_next (path); - node = gtk_tree_rbtree_next (tree, node); - } -} - -/** - * gtk_tree_view_map_expanded_rows: - * @tree_view: A `GtkTreeView` - * @func: (scope call): A function to be called - * @data: User data to be passed to the function. - * - * Calls @func on all expanded rows. - **/ -void -gtk_tree_view_map_expanded_rows (GtkTreeView *tree_view, - GtkTreeViewMappingFunc func, - gpointer user_data) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreePath *path; - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - g_return_if_fail (func != NULL); - - path = gtk_tree_path_new_first (); - - gtk_tree_view_map_expanded_rows_helper (tree_view, - priv->tree, - path, func, user_data); - - gtk_tree_path_free (path); -} - -/** - * gtk_tree_view_row_expanded: - * @tree_view: A `GtkTreeView`. - * @path: A `GtkTreePath` to test expansion state. - * - * Returns %TRUE if the node pointed to by @path is expanded in @tree_view. - * - * Returns: %TRUE if #path is expanded. - **/ -gboolean -gtk_tree_view_row_expanded (GtkTreeView *tree_view, - GtkTreePath *path) -{ - GtkTreeRBTree *tree; - GtkTreeRBNode *node; - - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); - g_return_val_if_fail (path != NULL, FALSE); - - _gtk_tree_view_find_node (tree_view, path, &tree, &node); - - if (node == NULL) - return FALSE; - - return (node->children != NULL); -} - -/** - * gtk_tree_view_get_reorderable: - * @tree_view: a `GtkTreeView` - * - * Retrieves whether the user can reorder the tree via drag-and-drop. See - * gtk_tree_view_set_reorderable(). - * - * Returns: %TRUE if the tree can be reordered. - **/ -gboolean -gtk_tree_view_get_reorderable (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); - - return priv->reorderable; -} - -/** - * gtk_tree_view_set_reorderable: - * @tree_view: A `GtkTreeView`. - * @reorderable: %TRUE, if the tree can be reordered. - * - * This function is a convenience function to allow you to reorder - * models that support the `GtkTreeDragSourceIface` and the - * `GtkTreeDragDestIface`. Both `GtkTreeStore` and `GtkListStore` support - * these. If @reorderable is %TRUE, then the user can reorder the - * model by dragging and dropping rows. The developer can listen to - * these changes by connecting to the model’s `GtkTreeModel::row-inserted` - * and `GtkTreeModel::row-deleted` signals. The reordering is implemented - * by setting up the tree view as a drag source and destination. - * Therefore, drag and drop can not be used in a reorderable view for any - * other purpose. - * - * This function does not give you any degree of control over the order -- any - * reordering is allowed. If more control is needed, you should probably - * handle drag and drop manually. - **/ -void -gtk_tree_view_set_reorderable (GtkTreeView *tree_view, - gboolean reorderable) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - - reorderable = reorderable != FALSE; - - if (priv->reorderable == reorderable) - return; - - if (reorderable) - { - GdkContentFormats *formats; - - formats = gdk_content_formats_new_for_gtype (GTK_TYPE_TREE_ROW_DATA); - - gtk_tree_view_enable_model_drag_source (tree_view, - GDK_BUTTON1_MASK, - formats, - GDK_ACTION_MOVE); - gtk_tree_view_enable_model_drag_dest (tree_view, - formats, - GDK_ACTION_MOVE); - gdk_content_formats_unref (formats); - } - else - { - gtk_tree_view_unset_rows_drag_source (tree_view); - gtk_tree_view_unset_rows_drag_dest (tree_view); - } - - priv->reorderable = reorderable; - - g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_REORDERABLE]); -} - -static void -gtk_tree_view_real_set_cursor (GtkTreeView *tree_view, - GtkTreePath *path, - SetCursorFlags flags) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - if (!(flags & CURSOR_INVALID) && priv->cursor_node) - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - - /* One cannot set the cursor on a separator. Also, if - * _gtk_tree_view_find_node returns TRUE, it ran out of tree - * before finding the tree and node belonging to path. The - * path maps to a non-existing path and we will silently bail out. - * We unset tree and node to avoid further processing. - */ - if (path == NULL || - row_is_separator (tree_view, NULL, path) - || _gtk_tree_view_find_node (tree_view, - path, - &priv->cursor_tree, - &priv->cursor_node)) - { - priv->cursor_tree = NULL; - priv->cursor_node = NULL; - } - - if (priv->cursor_node != NULL) - { - GtkTreeRBTree *new_tree = NULL; - GtkTreeRBNode *new_node = NULL; - - if ((flags & CLEAR_AND_SELECT) && !priv->modify_selection_pressed) - { - GtkTreeSelectMode mode = 0; - - if (priv->extend_selection_pressed) - mode |= GTK_TREE_SELECT_MODE_EXTEND; - - _gtk_tree_selection_internal_select_node (priv->selection, - priv->cursor_node, - priv->cursor_tree, - path, - mode, - FALSE); - } - - /* We have to re-find tree and node here again, somebody might have - * cleared the node or the whole tree in the GtkTreeSelection::changed - * callback. If the nodes differ we bail out here. - */ - _gtk_tree_view_find_node (tree_view, path, &new_tree, &new_node); - - if (priv->cursor_node == NULL || - priv->cursor_node != new_node) - return; - - if (flags & CLAMP_NODE) - { - gtk_tree_view_clamp_node_visible (tree_view, - priv->cursor_tree, - priv->cursor_node); - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - } - } - - if (!gtk_widget_in_destruction (GTK_WIDGET (tree_view))) - g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0); -} - -/** - * gtk_tree_view_get_cursor: - * @tree_view: A `GtkTreeView` - * @path: (out) (transfer full) (optional) (nullable): A pointer to be - * filled with the current cursor path - * @focus_column: (out) (transfer none) (optional) (nullable): A - * pointer to be filled with the current focus column - * - * Fills in @path and @focus_column with the current path and focus column. If - * the cursor isn’t currently set, then *@path will be %NULL. If no column - * currently has focus, then *@focus_column will be %NULL. - * - * The returned `GtkTreePath` must be freed with gtk_tree_path_free() when - * you are done with it. - **/ -void -gtk_tree_view_get_cursor (GtkTreeView *tree_view, - GtkTreePath **path, - GtkTreeViewColumn **focus_column) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - - if (path) - { - if (priv->cursor_node) - *path = _gtk_tree_path_new_from_rbtree (priv->cursor_tree, - priv->cursor_node); - else - *path = NULL; - } - - if (focus_column) - { - *focus_column = priv->focus_column; - } -} - -/** - * gtk_tree_view_set_cursor: - * @tree_view: A `GtkTreeView` - * @path: A `GtkTreePath` - * @focus_column: (nullable): A `GtkTreeViewColumn` - * @start_editing: %TRUE if the specified cell should start being edited. - * - * Sets the current keyboard focus to be at @path, and selects it. This is - * useful when you want to focus the user’s attention on a particular row. If - * @focus_column is not %NULL, then focus is given to the column specified by - * it. Additionally, if @focus_column is specified, and @start_editing is - * %TRUE, then editing should be started in the specified cell. - * This function is often followed by @gtk_widget_grab_focus (@tree_view) - * in order to give keyboard focus to the widget. Please note that editing - * can only happen when the widget is realized. - * - * If @path is invalid for @model, the current cursor (if any) will be unset - * and the function will return without failing. - **/ -void -gtk_tree_view_set_cursor (GtkTreeView *tree_view, - GtkTreePath *path, - GtkTreeViewColumn *focus_column, - gboolean start_editing) -{ - gtk_tree_view_set_cursor_on_cell (tree_view, path, focus_column, - NULL, start_editing); -} - -/** - * gtk_tree_view_set_cursor_on_cell: - * @tree_view: A `GtkTreeView` - * @path: A `GtkTreePath` - * @focus_column: (nullable): A `GtkTreeViewColumn` - * @focus_cell: (nullable): A `GtkCellRenderer` - * @start_editing: %TRUE if the specified cell should start being edited. - * - * Sets the current keyboard focus to be at @path, and selects it. This is - * useful when you want to focus the user’s attention on a particular row. If - * @focus_column is not %NULL, then focus is given to the column specified by - * it. If @focus_column and @focus_cell are not %NULL, and @focus_column - * contains 2 or more editable or activatable cells, then focus is given to - * the cell specified by @focus_cell. Additionally, if @focus_column is - * specified, and @start_editing is %TRUE, then editing should be started in - * the specified cell. This function is often followed by - * @gtk_widget_grab_focus (@tree_view) in order to give keyboard focus to the - * widget. Please note that editing can only happen when the widget is - * realized. - * - * If @path is invalid for @model, the current cursor (if any) will be unset - * and the function will return without failing. - **/ -void -gtk_tree_view_set_cursor_on_cell (GtkTreeView *tree_view, - GtkTreePath *path, - GtkTreeViewColumn *focus_column, - GtkCellRenderer *focus_cell, - gboolean start_editing) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - g_return_if_fail (path != NULL); - g_return_if_fail (focus_column == NULL || GTK_IS_TREE_VIEW_COLUMN (focus_column)); - - if (!priv->model) - return; - - if (focus_cell) - { - g_return_if_fail (focus_column); - g_return_if_fail (GTK_IS_CELL_RENDERER (focus_cell)); - } - - /* cancel the current editing, if it exists */ - if (priv->edited_column && - gtk_cell_area_get_edit_widget - (gtk_cell_layout_get_area (GTK_CELL_LAYOUT (priv->edited_column)))) - gtk_tree_view_stop_editing (tree_view, TRUE); - - gtk_tree_view_real_set_cursor (tree_view, path, CLEAR_AND_SELECT | CLAMP_NODE); - - if (focus_column && - gtk_tree_view_column_get_visible (focus_column)) - { -#ifndef G_DISABLE_CHECKS - GList *list; - gboolean column_in_tree = FALSE; - - for (list = priv->columns; list; list = list->next) - if (list->data == focus_column) - { - column_in_tree = TRUE; - break; - } - g_return_if_fail (column_in_tree); -#endif - _gtk_tree_view_set_focus_column (tree_view, focus_column); - if (focus_cell) - gtk_tree_view_column_focus_cell (focus_column, focus_cell); - if (start_editing) - gtk_tree_view_start_editing (tree_view, path, TRUE); - } -} - -/** - * gtk_tree_view_get_path_at_pos: - * @tree_view: A `GtkTreeView`. - * @x: The x position to be identified (relative to bin_window). - * @y: The y position to be identified (relative to bin_window). - * @path: (out) (optional) (nullable): A pointer to a `GtkTreePath` - * pointer to be filled in - * @column: (out) (transfer none) (optional) (nullable): A pointer to - * a `GtkTreeViewColumn` pointer to be filled in - * @cell_x: (out) (optional): A pointer where the X coordinate - * relative to the cell can be placed - * @cell_y: (out) (optional): A pointer where the Y coordinate - * relative to the cell can be placed - * - * Finds the path at the point (@x, @y), relative to bin_window coordinates. - * That is, @x and @y are relative to an events coordinates. Widget-relative - * coordinates must be converted using - * gtk_tree_view_convert_widget_to_bin_window_coords(). It is primarily for - * things like popup menus. If @path is non-%NULL, then it will be filled - * with the `GtkTreePath` at that point. This path should be freed with - * gtk_tree_path_free(). If @column is non-%NULL, then it will be filled - * with the column at that point. @cell_x and @cell_y return the coordinates - * relative to the cell background (i.e. the @background_area passed to - * gtk_cell_renderer_render()). This function is only meaningful if - * @tree_view is realized. Therefore this function will always return %FALSE - * if @tree_view is not realized or does not have a model. - * - * For converting widget coordinates (eg. the ones you get from - * GtkWidget::query-tooltip), please see - * gtk_tree_view_convert_widget_to_bin_window_coords(). - * - * Returns: %TRUE if a row exists at that coordinate. - **/ -gboolean -gtk_tree_view_get_path_at_pos (GtkTreeView *tree_view, - int x, - int y, - GtkTreePath **path, - GtkTreeViewColumn **column, - int *cell_x, - int *cell_y) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreeRBTree *tree; - GtkTreeRBNode *node; - int y_offset; - - g_return_val_if_fail (tree_view != NULL, FALSE); - - if (path) - *path = NULL; - if (column) - *column = NULL; - - if (priv->tree == NULL) - return FALSE; - - if (x > gtk_adjustment_get_upper (priv->hadjustment)) - return FALSE; - - if (x < 0 || y < 0) - return FALSE; - - if (column || cell_x) - { - GtkTreeViewColumn *tmp_column; - GtkTreeViewColumn *last_column = NULL; - GList *list; - int remaining_x = x; - gboolean found = FALSE; - gboolean rtl; - int width; - - rtl = (_gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); - for (list = (rtl ? g_list_last (priv->columns) : g_list_first (priv->columns)); - list; - list = (rtl ? list->prev : list->next)) - { - tmp_column = list->data; - - if (gtk_tree_view_column_get_visible (tmp_column) == FALSE) - continue; - - last_column = tmp_column; - width = gtk_tree_view_column_get_width (tmp_column); - if (remaining_x < width) - { - found = TRUE; - - if (column) - *column = tmp_column; - - if (cell_x) - *cell_x = remaining_x; - - break; - } - remaining_x -= width; - } - - /* If found is FALSE and there is a last_column, then it the remainder - * space is in that area - */ - if (!found) - { - if (last_column) - { - if (column) - *column = last_column; - - if (cell_x) - *cell_x = gtk_tree_view_column_get_width (last_column) + remaining_x; - } - else - { - return FALSE; - } - } - } - - y_offset = gtk_tree_rbtree_find_offset (priv->tree, - TREE_WINDOW_Y_TO_RBTREE_Y (priv, y), - &tree, &node); - - if (tree == NULL) - return FALSE; - - if (cell_y) - *cell_y = y_offset; - - if (path) - *path = _gtk_tree_path_new_from_rbtree (tree, node); - - return TRUE; -} - - -static inline int -gtk_tree_view_get_cell_area_height (GtkTreeView *tree_view, - GtkTreeRBNode *node) -{ - int expander_size = gtk_tree_view_get_expander_size (tree_view); - int height; - - /* The "cell" areas are the cell_area passed in to gtk_cell_renderer_render(), - * i.e. just the cells, no spacing. - * - * The cell area height is at least expander_size - vertical_separator. - * For regular nodes, the height is then at least expander_size. We should - * be able to enforce the expander_size minimum here, because this - * function will not be called for irregular (e.g. separator) rows. - */ - height = gtk_tree_view_get_row_height (tree_view, node); - if (height < expander_size) - height = expander_size; - - return height; -} - -static inline int -gtk_tree_view_get_cell_area_y_offset (GtkTreeView *tree_view, - GtkTreeRBTree *tree, - GtkTreeRBNode *node) -{ - int offset; - - offset = gtk_tree_view_get_row_y_offset (tree_view, tree, node); - - return offset; -} - -/** - * gtk_tree_view_get_cell_area: - * @tree_view: a `GtkTreeView` - * @path: (nullable): a `GtkTreePath` for the row, or %NULL to get only horizontal coordinates - * @column: (nullable): a `GtkTreeViewColumn` for the column, or %NULL to get only vertical coordinates - * @rect: (out): rectangle to fill with cell rect - * - * Fills the bounding rectangle in bin_window coordinates for the cell at the - * row specified by @path and the column specified by @column. If @path is - * %NULL, or points to a path not currently displayed, the @y and @height fields - * of the rectangle will be filled with 0. If @column is %NULL, the @x and @width - * fields will be filled with 0. The sum of all cell rects does not cover the - * entire tree; there are extra pixels in between rows, for example. The - * returned rectangle is equivalent to the @cell_area passed to - * gtk_cell_renderer_render(). This function is only valid if @tree_view is - * realized. - **/ -void -gtk_tree_view_get_cell_area (GtkTreeView *tree_view, - GtkTreePath *path, - GtkTreeViewColumn *column, - GdkRectangle *rect) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreeRBTree *tree = NULL; - GtkTreeRBNode *node = NULL; - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - g_return_if_fail (column == NULL || GTK_IS_TREE_VIEW_COLUMN (column)); - g_return_if_fail (rect != NULL); - g_return_if_fail (!column || gtk_tree_view_column_get_tree_view (column) == (GtkWidget *) tree_view); - g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view))); - - rect->x = 0; - rect->y = 0; - rect->width = 0; - rect->height = 0; - - if (column) - { - rect->x = gtk_tree_view_column_get_x_offset (column) + _TREE_VIEW_HORIZONTAL_SEPARATOR / 2; - rect->width = gtk_tree_view_column_get_width (column) - _TREE_VIEW_HORIZONTAL_SEPARATOR; - } - - if (path) - { - gboolean ret = _gtk_tree_view_find_node (tree_view, path, &tree, &node); - - /* Get vertical coords */ - if ((!ret && tree == NULL) || ret) - return; - - if (row_is_separator (tree_view, NULL, path)) - { - /* There isn't really a "cell area" for separator, so we - * return the y, height values for background area instead. - */ - rect->y = gtk_tree_view_get_row_y_offset (tree_view, tree, node); - rect->height = gtk_tree_view_get_row_height (tree_view, node); - } - else - { - rect->y = gtk_tree_view_get_cell_area_y_offset (tree_view, tree, node); - rect->height = gtk_tree_view_get_cell_area_height (tree_view, node); - } - - if (column && - gtk_tree_view_is_expander_column (tree_view, column)) - { - int depth = gtk_tree_path_get_depth (path); - gboolean rtl; - - rtl = _gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL; - - if (!rtl) - rect->x += (depth - 1) * priv->level_indentation; - rect->width -= (depth - 1) * priv->level_indentation; - - if (gtk_tree_view_draw_expanders (tree_view)) - { - int expander_size = gtk_tree_view_get_expander_size (tree_view); - if (!rtl) - rect->x += depth * expander_size; - rect->width -= depth * expander_size; - } - - rect->width = MAX (rect->width, 0); - } - } -} - -static inline int -gtk_tree_view_get_row_height (GtkTreeView *tree_view, - GtkTreeRBNode *node) -{ - int expander_size = gtk_tree_view_get_expander_size (tree_view); - int height; - - /* The "background" areas of all rows/cells add up to cover the entire tree. - * The background includes all inter-row and inter-cell spacing. - * - * If the row pointed at by node does not have a height set, we default - * to expander_size, which is the minimum height for regular nodes. - * Non-regular nodes (e.g. separators) can have a height set smaller - * than expander_size and should not be overruled here. - */ - height = GTK_TREE_RBNODE_GET_HEIGHT (node); - if (height <= 0) - height = expander_size; - - return height; -} - -static inline int -gtk_tree_view_get_row_y_offset (GtkTreeView *tree_view, - GtkTreeRBTree *tree, - GtkTreeRBNode *node) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - int offset; - - offset = gtk_tree_rbtree_node_find_offset (tree, node); - - return RBTREE_Y_TO_TREE_WINDOW_Y (priv, offset); -} - -/** - * gtk_tree_view_get_background_area: - * @tree_view: a `GtkTreeView` - * @path: (nullable): a `GtkTreePath` for the row, or %NULL to get only horizontal coordinates - * @column: (nullable): a `GtkTreeViewColumn` for the column, or %NULL to get only vertical coordinates - * @rect: (out): rectangle to fill with cell background rect - * - * Fills the bounding rectangle in bin_window coordinates for the cell at the - * row specified by @path and the column specified by @column. If @path is - * %NULL, or points to a node not found in the tree, the @y and @height fields of - * the rectangle will be filled with 0. If @column is %NULL, the @x and @width - * fields will be filled with 0. The returned rectangle is equivalent to the - * @background_area passed to gtk_cell_renderer_render(). These background - * areas tile to cover the entire bin window. Contrast with the @cell_area, - * returned by gtk_tree_view_get_cell_area(), which returns only the cell - * itself, excluding surrounding borders and the tree expander area. - * - **/ -void -gtk_tree_view_get_background_area (GtkTreeView *tree_view, - GtkTreePath *path, - GtkTreeViewColumn *column, - GdkRectangle *rect) -{ - GtkTreeRBTree *tree = NULL; - GtkTreeRBNode *node = NULL; - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - g_return_if_fail (column == NULL || GTK_IS_TREE_VIEW_COLUMN (column)); - g_return_if_fail (rect != NULL); - - rect->x = 0; - rect->y = 0; - rect->width = 0; - rect->height = 0; - - if (path) - { - /* Get vertical coords */ - - if (!_gtk_tree_view_find_node (tree_view, path, &tree, &node) && - tree == NULL) - return; - - rect->y = gtk_tree_view_get_row_y_offset (tree_view, tree, node); - rect->height = gtk_tree_view_get_row_height (tree_view, node); - } - - if (column) - { - int x2 = 0; - - gtk_tree_view_get_background_xrange (tree_view, tree, column, &rect->x, &x2); - rect->width = x2 - rect->x; - } -} - -/** - * gtk_tree_view_get_visible_rect: - * @tree_view: a `GtkTreeView` - * @visible_rect: (out): rectangle to fill - * - * Fills @visible_rect with the currently-visible region of the - * buffer, in tree coordinates. Convert to bin_window coordinates with - * gtk_tree_view_convert_tree_to_bin_window_coords(). - * Tree coordinates start at 0,0 for row 0 of the tree, and cover the entire - * scrollable area of the tree. - **/ -void -gtk_tree_view_get_visible_rect (GtkTreeView *tree_view, - GdkRectangle *visible_rect) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkAllocation allocation; - GtkWidget *widget; - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - - widget = GTK_WIDGET (tree_view); - - if (visible_rect) - { - gtk_widget_get_allocation (widget, &allocation); - visible_rect->x = gtk_adjustment_get_value (priv->hadjustment); - visible_rect->y = gtk_adjustment_get_value (priv->vadjustment); - visible_rect->width = allocation.width; - visible_rect->height = allocation.height - gtk_tree_view_get_effective_header_height (tree_view); - } -} - -/** - * gtk_tree_view_convert_widget_to_tree_coords: - * @tree_view: a `GtkTreeView` - * @wx: X coordinate relative to the widget - * @wy: Y coordinate relative to the widget - * @tx: (out): return location for tree X coordinate - * @ty: (out): return location for tree Y coordinate - * - * Converts widget coordinates to coordinates for the - * tree (the full scrollable area of the tree). - **/ -void -gtk_tree_view_convert_widget_to_tree_coords (GtkTreeView *tree_view, - int wx, - int wy, - int *tx, - int *ty) -{ - int x, y; - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - - gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, - wx, wy, - &x, &y); - gtk_tree_view_convert_bin_window_to_tree_coords (tree_view, - x, y, - tx, ty); -} - -/** - * gtk_tree_view_convert_tree_to_widget_coords: - * @tree_view: a `GtkTreeView` - * @tx: X coordinate relative to the tree - * @ty: Y coordinate relative to the tree - * @wx: (out): return location for widget X coordinate - * @wy: (out): return location for widget Y coordinate - * - * Converts tree coordinates (coordinates in full scrollable area of the tree) - * to widget coordinates. - **/ -void -gtk_tree_view_convert_tree_to_widget_coords (GtkTreeView *tree_view, - int tx, - int ty, - int *wx, - int *wy) -{ - int x, y; - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - - gtk_tree_view_convert_tree_to_bin_window_coords (tree_view, - tx, ty, - &x, &y); - gtk_tree_view_convert_bin_window_to_widget_coords (tree_view, - x, y, - wx, wy); -} - -/** - * gtk_tree_view_convert_widget_to_bin_window_coords: - * @tree_view: a `GtkTreeView` - * @wx: X coordinate relative to the widget - * @wy: Y coordinate relative to the widget - * @bx: (out): return location for bin_window X coordinate - * @by: (out): return location for bin_window Y coordinate - * - * Converts widget coordinates to coordinates for the bin_window. - **/ -void -gtk_tree_view_convert_widget_to_bin_window_coords (GtkTreeView *tree_view, - int wx, - int wy, - int *bx, - int *by) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - - if (bx) - *bx = wx + gtk_adjustment_get_value (priv->hadjustment); - if (by) - *by = wy - gtk_tree_view_get_effective_header_height (tree_view); -} - -/** - * gtk_tree_view_convert_bin_window_to_widget_coords: - * @tree_view: a `GtkTreeView` - * @bx: bin_window X coordinate - * @by: bin_window Y coordinate - * @wx: (out): return location for widget X coordinate - * @wy: (out): return location for widget Y coordinate - * - * Converts bin_window coordinates to widget relative coordinates. - **/ -void -gtk_tree_view_convert_bin_window_to_widget_coords (GtkTreeView *tree_view, - int bx, - int by, - int *wx, - int *wy) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - - if (wx) - *wx = bx - gtk_adjustment_get_value (priv->hadjustment); - if (wy) - *wy = by + gtk_tree_view_get_effective_header_height (tree_view); -} - -/** - * gtk_tree_view_convert_tree_to_bin_window_coords: - * @tree_view: a `GtkTreeView` - * @tx: tree X coordinate - * @ty: tree Y coordinate - * @bx: (out): return location for X coordinate relative to bin_window - * @by: (out): return location for Y coordinate relative to bin_window - * - * Converts tree coordinates (coordinates in full scrollable area of the tree) - * to bin_window coordinates. - **/ -void -gtk_tree_view_convert_tree_to_bin_window_coords (GtkTreeView *tree_view, - int tx, - int ty, - int *bx, - int *by) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - - if (bx) - *bx = tx; - if (by) - *by = ty - priv->dy; -} - -/** - * gtk_tree_view_convert_bin_window_to_tree_coords: - * @tree_view: a `GtkTreeView` - * @bx: X coordinate relative to bin_window - * @by: Y coordinate relative to bin_window - * @tx: (out): return location for tree X coordinate - * @ty: (out): return location for tree Y coordinate - * - * Converts bin_window coordinates to coordinates for the - * tree (the full scrollable area of the tree). - **/ -void -gtk_tree_view_convert_bin_window_to_tree_coords (GtkTreeView *tree_view, - int bx, - int by, - int *tx, - int *ty) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - - if (tx) - *tx = bx; - if (ty) - *ty = by + priv->dy; -} - - - -/** - * gtk_tree_view_get_visible_range: - * @tree_view: A `GtkTreeView` - * @start_path: (out) (optional): Return location for start of region - * @end_path: (out) (optional): Return location for end of region - * - * Sets @start_path and @end_path to be the first and last visible path. - * Note that there may be invisible paths in between. - * - * The paths should be freed with gtk_tree_path_free() after use. - * - * Returns: %TRUE, if valid paths were placed in @start_path and @end_path. - */ -gboolean -gtk_tree_view_get_visible_range (GtkTreeView *tree_view, - GtkTreePath **start_path, - GtkTreePath **end_path) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreeRBTree *tree; - GtkTreeRBNode *node; - gboolean retval; - - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); - - if (!priv->tree) - return FALSE; - - retval = TRUE; - - if (start_path) - { - gtk_tree_rbtree_find_offset (priv->tree, - TREE_WINDOW_Y_TO_RBTREE_Y (priv, 0), - &tree, &node); - if (node) - *start_path = _gtk_tree_path_new_from_rbtree (tree, node); - else - retval = FALSE; - } - - if (end_path) - { - int y; - - if (gtk_tree_view_get_height (tree_view) < gtk_adjustment_get_page_size (priv->vadjustment)) - y = gtk_tree_view_get_height (tree_view) - 1; - else - y = TREE_WINDOW_Y_TO_RBTREE_Y (priv, gtk_adjustment_get_page_size (priv->vadjustment)) - 1; - - gtk_tree_rbtree_find_offset (priv->tree, y, &tree, &node); - if (node) - *end_path = _gtk_tree_path_new_from_rbtree (tree, node); - else - retval = FALSE; - } - - return retval; -} - -/** - * gtk_tree_view_is_blank_at_pos: - * @tree_view: A `GtkTreeView` - * @x: The x position to be identified (relative to bin_window) - * @y: The y position to be identified (relative to bin_window) - * @path: (out) (optional) (nullable): A pointer to a `GtkTreePath` pointer to - * be filled in - * @column: (out) (transfer none) (optional) (nullable): A pointer to a - * `GtkTreeViewColumn` pointer to be filled in - * @cell_x: (out) (optional): A pointer where the X coordinate relative to the - * cell can be placed - * @cell_y: (out) (optional): A pointer where the Y coordinate relative to the - * cell can be placed - * - * Determine whether the point (@x, @y) in @tree_view is blank, that is no - * cell content nor an expander arrow is drawn at the location. If so, the - * location can be considered as the background. You might wish to take - * special action on clicks on the background, such as clearing a current - * selection, having a custom context menu or starting rubber banding. - * - * The @x and @y coordinate that are provided must be relative to bin_window - * coordinates. Widget-relative coordinates must be converted using - * gtk_tree_view_convert_widget_to_bin_window_coords(). - * - * For converting widget coordinates (eg. the ones you get from - * GtkWidget::query-tooltip), please see - * gtk_tree_view_convert_widget_to_bin_window_coords(). - * - * The @path, @column, @cell_x and @cell_y arguments will be filled in - * likewise as for gtk_tree_view_get_path_at_pos(). Please see - * gtk_tree_view_get_path_at_pos() for more information. - * - * Returns: %TRUE if the area at the given coordinates is blank, - * %FALSE otherwise. - */ -gboolean -gtk_tree_view_is_blank_at_pos (GtkTreeView *tree_view, - int x, - int y, - GtkTreePath **path, - GtkTreeViewColumn **column, - int *cell_x, - int *cell_y) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreeRBTree *tree; - GtkTreeRBNode *node; - GtkTreeIter iter; - GtkTreePath *real_path; - GtkTreeViewColumn *real_column; - GdkRectangle cell_area, background_area; - - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); - - if (!gtk_tree_view_get_path_at_pos (tree_view, x, y, - &real_path, &real_column, - cell_x, cell_y)) - /* If there's no path here, it is blank */ - return TRUE; - - if (path) - *path = real_path; - - if (column) - *column = real_column; - - gtk_tree_model_get_iter (priv->model, &iter, real_path); - _gtk_tree_view_find_node (tree_view, real_path, &tree, &node); - - /* Check if there's an expander arrow at (x, y) */ - if (real_column == priv->expander_column - && gtk_tree_view_draw_expanders (tree_view)) - { - gboolean over_arrow; - - over_arrow = coords_are_over_arrow (tree_view, tree, node, x, y); - - if (over_arrow) - { - if (!path) - gtk_tree_path_free (real_path); - return FALSE; - } - } - - /* Otherwise, have the column see if there's a cell at (x, y) */ - gtk_tree_view_column_cell_set_cell_data (real_column, - priv->model, - &iter, - GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_PARENT), - node->children ? TRUE : FALSE); - - gtk_tree_view_get_background_area (tree_view, real_path, real_column, - &background_area); - gtk_tree_view_get_cell_area (tree_view, real_path, real_column, - &cell_area); - - if (!path) - gtk_tree_path_free (real_path); - - return _gtk_tree_view_column_is_blank_at_pos (real_column, - &cell_area, - &background_area, - x, y); -} - -static void -unset_reorderable (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - if (priv->reorderable) - { - priv->reorderable = FALSE; - g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_REORDERABLE]); - } -} - -/** - * gtk_tree_view_enable_model_drag_source: - * @tree_view: a `GtkTreeView` - * @start_button_mask: Mask of allowed buttons to start drag - * @formats: the target formats that the drag will support - * @actions: the bitmask of possible actions for a drag from this - * widget - * - * Turns @tree_view into a drag source for automatic DND. Calling this - * method sets `GtkTreeView`:reorderable to %FALSE. - **/ -void -gtk_tree_view_enable_model_drag_source (GtkTreeView *tree_view, - GdkModifierType start_button_mask, - GdkContentFormats *formats, - GdkDragAction actions) -{ - TreeViewDragInfo *di; - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - - di = ensure_info (tree_view); - - di->source_formats = gdk_content_formats_ref (formats); - di->source_actions = actions; - di->drag = NULL; - - di->start_button_mask = start_button_mask; - di->source_set = TRUE; - - unset_reorderable (tree_view); -} - -/** - * gtk_tree_view_enable_model_drag_dest: - * @tree_view: a `GtkTreeView` - * @formats: the target formats that the drag will support - * @actions: the bitmask of possible actions for a drag from this - * widget - * - * Turns @tree_view into a drop destination for automatic DND. Calling - * this method sets `GtkTreeView`:reorderable to %FALSE. - **/ -void -gtk_tree_view_enable_model_drag_dest (GtkTreeView *tree_view, - GdkContentFormats *formats, - GdkDragAction actions) -{ - TreeViewDragInfo *di; - GtkCssNode *widget_node; - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - - di = ensure_info (tree_view); - di->dest_set = TRUE; - - di->dest = gtk_drop_target_async_new (gdk_content_formats_ref (formats), actions); - g_signal_connect (di->dest, "drag-leave", G_CALLBACK (gtk_tree_view_drag_leave), tree_view); - g_signal_connect (di->dest, "drag-enter", G_CALLBACK (gtk_tree_view_drag_motion), tree_view); - g_signal_connect (di->dest, "drag-motion", G_CALLBACK (gtk_tree_view_drag_motion), tree_view); - g_signal_connect (di->dest, "drop", G_CALLBACK (gtk_tree_view_drag_drop), tree_view); - gtk_widget_add_controller (GTK_WIDGET (tree_view), GTK_EVENT_CONTROLLER (di->dest)); - g_object_ref (di->dest); - - widget_node = gtk_widget_get_css_node (GTK_WIDGET (tree_view)); - di->cssnode = gtk_css_node_new (); - gtk_css_node_set_name (di->cssnode, g_quark_from_static_string ("dndtarget")); - gtk_css_node_set_parent (di->cssnode, widget_node); - gtk_css_node_set_state (di->cssnode, gtk_css_node_get_state (widget_node)); - g_object_unref (di->cssnode); - - unset_reorderable (tree_view); -} - -/** - * gtk_tree_view_unset_rows_drag_source: - * @tree_view: a `GtkTreeView` - * - * Undoes the effect of - * gtk_tree_view_enable_model_drag_source(). Calling this method sets - * `GtkTreeView`:reorderable to %FALSE. - **/ -void -gtk_tree_view_unset_rows_drag_source (GtkTreeView *tree_view) -{ - TreeViewDragInfo *di; - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - - di = get_info (tree_view); - - if (di) - { - if (di->source_set) - { - g_clear_pointer (&di->source_formats, gdk_content_formats_unref); - di->source_set = FALSE; - } - - if (!di->dest_set && !di->source_set) - remove_info (tree_view); - } - - unset_reorderable (tree_view); -} - -/** - * gtk_tree_view_unset_rows_drag_dest: - * @tree_view: a `GtkTreeView` - * - * Undoes the effect of - * gtk_tree_view_enable_model_drag_dest(). Calling this method sets - * `GtkTreeView`:reorderable to %FALSE. - **/ -void -gtk_tree_view_unset_rows_drag_dest (GtkTreeView *tree_view) -{ - TreeViewDragInfo *di; - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - - di = get_info (tree_view); - - if (di) - { - if (di->dest_set) - { - gtk_widget_remove_controller (GTK_WIDGET (tree_view), GTK_EVENT_CONTROLLER (di->dest)); - di->dest = NULL; - di->dest_set = FALSE; - - gtk_css_node_set_parent (di->cssnode, NULL); - di->cssnode = NULL; - } - - if (!di->dest_set && !di->source_set) - remove_info (tree_view); - } - - unset_reorderable (tree_view); -} - -/** - * gtk_tree_view_set_drag_dest_row: - * @tree_view: a `GtkTreeView` - * @path: (nullable): The path of the row to highlight - * @pos: Specifies whether to drop before, after or into the row - * - * Sets the row that is highlighted for feedback. - * If @path is %NULL, an existing highlight is removed. - */ -void -gtk_tree_view_set_drag_dest_row (GtkTreeView *tree_view, - GtkTreePath *path, - GtkTreeViewDropPosition pos) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreePath *current_dest; - - /* Note; this function is exported to allow a custom DND - * implementation, so it can't touch TreeViewDragInfo - */ - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - - current_dest = NULL; - - if (priv->drag_dest_row) - { - current_dest = gtk_tree_row_reference_get_path (priv->drag_dest_row); - gtk_tree_row_reference_free (priv->drag_dest_row); - } - - /* special case a drop on an empty model */ - priv->empty_view_drop = 0; - - if (pos == GTK_TREE_VIEW_DROP_BEFORE && path - && gtk_tree_path_get_depth (path) == 1 - && gtk_tree_path_get_indices (path)[0] == 0) - { - int n_children; - - n_children = gtk_tree_model_iter_n_children (priv->model, - NULL); - - if (!n_children) - priv->empty_view_drop = 1; - } - - priv->drag_dest_pos = pos; - - if (path) - { - priv->drag_dest_row = - gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), priv->model, path); - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - } - else - priv->drag_dest_row = NULL; - - if (current_dest) - { - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - - gtk_tree_path_free (current_dest); - } -} - -/** - * gtk_tree_view_get_drag_dest_row: - * @tree_view: a `GtkTreeView` - * @path: (out) (optional) (nullable): Return location for the path of the highlighted row - * @pos: (out) (optional): Return location for the drop position - * - * Gets information about the row that is highlighted for feedback. - **/ -void -gtk_tree_view_get_drag_dest_row (GtkTreeView *tree_view, - GtkTreePath **path, - GtkTreeViewDropPosition *pos) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - - if (path) - { - if (priv->drag_dest_row) - *path = gtk_tree_row_reference_get_path (priv->drag_dest_row); - else - { - if (priv->empty_view_drop) - *path = gtk_tree_path_new_from_indices (0, -1); - else - *path = NULL; - } - } - - if (pos) - *pos = priv->drag_dest_pos; -} - -/** - * gtk_tree_view_get_dest_row_at_pos: - * @tree_view: a `GtkTreeView` - * @drag_x: the position to determine the destination row for - * @drag_y: the position to determine the destination row for - * @path: (out) (optional) (nullable): Return location for the path of - * the highlighted row - * @pos: (out) (optional): Return location for the drop position, or - * %NULL - * - * Determines the destination row for a given position. @drag_x and - * @drag_y are expected to be in widget coordinates. This function is only - * meaningful if @tree_view is realized. Therefore this function will always - * return %FALSE if @tree_view is not realized or does not have a model. - * - * Returns: whether there is a row at the given position, %TRUE if this - * is indeed the case. - **/ -gboolean -gtk_tree_view_get_dest_row_at_pos (GtkTreeView *tree_view, - int drag_x, - int drag_y, - GtkTreePath **path, - GtkTreeViewDropPosition *pos) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - int cell_y; - int bin_x, bin_y; - double offset_into_row; - double fourth; - GdkRectangle cell; - GtkTreeViewColumn *column = NULL; - GtkTreePath *tmp_path = NULL; - - /* Note; this function is exported to allow a custom DND - * implementation, so it can't touch TreeViewDragInfo - */ - - g_return_val_if_fail (tree_view != NULL, FALSE); - g_return_val_if_fail (drag_x >= 0, FALSE); - g_return_val_if_fail (drag_y >= 0, FALSE); - - if (path) - *path = NULL; - - if (priv->tree == NULL) - return FALSE; - - /* If in the top fourth of a row, we drop before that row; if - * in the bottom fourth, drop after that row; if in the middle, - * and the row has children, drop into the row. - */ - gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, drag_x, drag_y, - &bin_x, &bin_y); - - if (!gtk_tree_view_get_path_at_pos (tree_view, - bin_x, - bin_y, - &tmp_path, - &column, - NULL, - &cell_y)) - return FALSE; - - gtk_tree_view_get_background_area (tree_view, tmp_path, column, - &cell); - - offset_into_row = cell_y; - - if (path) - *path = tmp_path; - else - gtk_tree_path_free (tmp_path); - - tmp_path = NULL; - - fourth = cell.height / 4.0; - - if (pos) - { - if (offset_into_row < fourth) - { - *pos = GTK_TREE_VIEW_DROP_BEFORE; - } - else if (offset_into_row < (cell.height / 2.0)) - { - *pos = GTK_TREE_VIEW_DROP_INTO_OR_BEFORE; - } - else if (offset_into_row < cell.height - fourth) - { - *pos = GTK_TREE_VIEW_DROP_INTO_OR_AFTER; - } - else - { - *pos = GTK_TREE_VIEW_DROP_AFTER; - } - } - - return TRUE; -} - - -static void -gtk_treeview_snapshot_border (GtkSnapshot *snapshot, - const graphene_rect_t *rect) -{ - GskRoundedRect rounded; - - gsk_rounded_rect_init_from_rect (&rounded, rect, 0); - -#define BLACK { 0, 0, 0, 1 } - gtk_snapshot_append_border (snapshot, - &rounded, - (float[4]) { 1, 1, 1, 1 }, - (GdkRGBA[4]) { BLACK, BLACK, BLACK, BLACK }); -#undef BLACK -} - -/* KEEP IN SYNC WITH GTK_TREE_VIEW_BIN_EXPOSE */ -/** - * gtk_tree_view_create_row_drag_icon: - * @tree_view: a `GtkTreeView` - * @path: a `GtkTreePath` in @tree_view - * - * Creates a `cairo_surface_t` representation of the row at @path. - * This image is used for a drag icon. - * - * Returns: (transfer full) (nullable): a newly-allocated surface of the drag icon. - **/ -GdkPaintable * -gtk_tree_view_create_row_drag_icon (GtkTreeView *tree_view, - GtkTreePath *path) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreeIter iter; - GtkTreeRBTree *tree; - GtkTreeRBNode *node; - GtkStyleContext *context; - int cell_offset; - GList *list; - GdkRectangle background_area; - GtkWidget *widget; - GtkSnapshot *snapshot; - GdkPaintable *paintable; - int depth; - /* start drawing inside the black outline */ - int x = 1, y = 1; - int bin_window_width; - gboolean is_separator = FALSE; - gboolean rtl; - - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL); - g_return_val_if_fail (path != NULL, NULL); - - widget = GTK_WIDGET (tree_view); - - if (!gtk_widget_get_realized (widget)) - return NULL; - - depth = gtk_tree_path_get_depth (path); - - _gtk_tree_view_find_node (tree_view, - path, - &tree, - &node); - - if (tree == NULL) - return NULL; - - if (!gtk_tree_model_get_iter (priv->model, - &iter, - path)) - return NULL; - - context = gtk_widget_get_style_context (widget); - - is_separator = row_is_separator (tree_view, &iter, NULL); - - cell_offset = x; - - background_area.y = y; - background_area.height = gtk_tree_view_get_row_height (tree_view, node); - - bin_window_width = gtk_widget_get_width (GTK_WIDGET (tree_view)); - - snapshot = gtk_snapshot_new (); - - gtk_snapshot_render_background (snapshot, context, - 0, 0, - bin_window_width + 2, - background_area.height + 2); - - rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL; - - for (list = (rtl ? g_list_last (priv->columns) : g_list_first (priv->columns)); - list; - list = (rtl ? list->prev : list->next)) - { - GtkTreeViewColumn *column = list->data; - GdkRectangle cell_area; - - if (!gtk_tree_view_column_get_visible (column)) - continue; - - gtk_tree_view_column_cell_set_cell_data (column, priv->model, &iter, - GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_PARENT), - node->children?TRUE:FALSE); - - background_area.x = cell_offset; - background_area.width = gtk_tree_view_column_get_width (column); - - cell_area = background_area; - - if (gtk_tree_view_is_expander_column (tree_view, column)) - { - if (!rtl) - cell_area.x += (depth - 1) * priv->level_indentation; - cell_area.width -= (depth - 1) * priv->level_indentation; - - if (gtk_tree_view_draw_expanders (tree_view)) - { - int expander_size = gtk_tree_view_get_expander_size (tree_view); - if (!rtl) - cell_area.x += depth * expander_size; - cell_area.width -= depth * expander_size; - } - } - - if (gtk_tree_view_column_cell_is_visible (column)) - { - if (is_separator) - { - GdkRGBA color; - - gtk_style_context_save (context); - gtk_style_context_add_class (context, "separator"); - - gtk_style_context_get_color (context, &color); - gtk_snapshot_append_color (snapshot, - &color, - &GRAPHENE_RECT_INIT( - cell_area.x, - cell_area.y + cell_area.height / 2, - cell_area.x + cell_area.width, - 1 - )); - - gtk_style_context_restore (context); - } - else - { - gtk_tree_view_column_cell_snapshot (column, - snapshot, - &background_area, - &cell_area, - 0, FALSE); - } - } - cell_offset += gtk_tree_view_column_get_width (column); - } - - gtk_treeview_snapshot_border (snapshot, - &GRAPHENE_RECT_INIT(0, 0, bin_window_width + 2, background_area.height + 2)); - - paintable = gtk_snapshot_free_to_paintable (snapshot, NULL); - - return paintable; -} - - -/* - * Interactive search - */ - -/** - * gtk_tree_view_set_enable_search: - * @tree_view: A `GtkTreeView` - * @enable_search: %TRUE, if the user can search interactively - * - * If @enable_search is set, then the user can type in text to search through - * the tree interactively (this is sometimes called "typeahead find"). - * - * Note that even if this is %FALSE, the user can still initiate a search - * using the “start-interactive-search” key binding. - */ -void -gtk_tree_view_set_enable_search (GtkTreeView *tree_view, - gboolean enable_search) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - - enable_search = !!enable_search; - - if (priv->enable_search != enable_search) - { - priv->enable_search = enable_search; - g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_ENABLE_SEARCH]); - } -} - -/** - * gtk_tree_view_get_enable_search: - * @tree_view: A `GtkTreeView` - * - * Returns whether or not the tree allows to start interactive searching - * by typing in text. - * - * Returns: whether or not to let the user search interactively - */ -gboolean -gtk_tree_view_get_enable_search (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); - - return priv->enable_search; -} - - -/** - * gtk_tree_view_get_search_column: - * @tree_view: A `GtkTreeView` - * - * Gets the column searched on by the interactive search code. - * - * Returns: the column the interactive search code searches in. - */ -int -gtk_tree_view_get_search_column (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1); - - return priv->search_column; -} - -/** - * gtk_tree_view_set_search_column: - * @tree_view: A `GtkTreeView` - * @column: the column of the model to search in, or -1 to disable searching - * - * Sets @column as the column where the interactive search code should - * search in for the current model. - * - * If the search column is set, users can use the “start-interactive-search” - * key binding to bring up search popup. The enable-search property controls - * whether simply typing text will also start an interactive search. - * - * Note that @column refers to a column of the current model. The search - * column is reset to -1 when the model is changed. - */ -void -gtk_tree_view_set_search_column (GtkTreeView *tree_view, - int column) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - g_return_if_fail (column >= -1); - - if (priv->search_column == column) - return; - - priv->search_column = column; - g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_SEARCH_COLUMN]); -} - -/** - * gtk_tree_view_get_search_equal_func: (skip) - * @tree_view: A `GtkTreeView` - * - * Returns the compare function currently in use. - * - * Returns: the currently used compare function for the search code. - */ - -GtkTreeViewSearchEqualFunc -gtk_tree_view_get_search_equal_func (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL); - - return priv->search_equal_func; -} - -/** - * gtk_tree_view_set_search_equal_func: - * @tree_view: A `GtkTreeView` - * @search_equal_func: the compare function to use during the search - * @search_user_data: (nullable): user data to pass to @search_equal_func - * @search_destroy: (nullable): Destroy notifier for @search_user_data - * - * Sets the compare function for the interactive search capabilities; note - * that somewhat like strcmp() returning 0 for equality - * `GtkTreeView`SearchEqualFunc returns %FALSE on matches. - **/ -void -gtk_tree_view_set_search_equal_func (GtkTreeView *tree_view, - GtkTreeViewSearchEqualFunc search_equal_func, - gpointer search_user_data, - GDestroyNotify search_destroy) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - g_return_if_fail (search_equal_func != NULL); - - if (priv->search_destroy) - priv->search_destroy (priv->search_user_data); - - priv->search_equal_func = search_equal_func; - priv->search_user_data = search_user_data; - priv->search_destroy = search_destroy; - if (priv->search_equal_func == NULL) - priv->search_equal_func = gtk_tree_view_search_equal_func; -} - -/** - * gtk_tree_view_get_search_entry: - * @tree_view: A `GtkTreeView` - * - * Returns the `GtkEntry` which is currently in use as interactive search - * entry for @tree_view. In case the built-in entry is being used, %NULL - * will be returned. - * - * Returns: (transfer none) (nullable): the entry currently in use as search entry. - */ -GtkEditable * -gtk_tree_view_get_search_entry (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL); - - if (priv->search_custom_entry_set) - return GTK_EDITABLE (priv->search_entry); - - return NULL; -} - -/** - * gtk_tree_view_set_search_entry: - * @tree_view: A `GtkTreeView` - * @entry: (nullable): the entry the interactive search code of @tree_view should use - * - * Sets the entry which the interactive search code will use for this - * @tree_view. This is useful when you want to provide a search entry - * in our interface at all time at a fixed position. Passing %NULL for - * @entry will make the interactive search code use the built-in popup - * entry again. - */ -void -gtk_tree_view_set_search_entry (GtkTreeView *tree_view, - GtkEditable *entry) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - g_return_if_fail (entry == NULL || GTK_IS_ENTRY (entry) || GTK_IS_SEARCH_ENTRY (entry)); - - if (priv->search_custom_entry_set) - { - if (priv->search_entry_changed_id) - { - g_signal_handler_disconnect (priv->search_entry, - priv->search_entry_changed_id); - priv->search_entry_changed_id = 0; - } - - g_signal_handlers_disconnect_by_func (gtk_entry_get_key_controller (GTK_ENTRY (priv->search_entry)), - G_CALLBACK (gtk_tree_view_search_key_pressed), - tree_view); - - g_object_unref (priv->search_entry); - } - else if (priv->search_popover) - { - gtk_tree_view_destroy_search_popover (tree_view); - } - - if (entry) - { - GtkEventController *controller; - - priv->search_entry = GTK_WIDGET (g_object_ref (entry)); - priv->search_custom_entry_set = TRUE; - - if (priv->search_entry_changed_id == 0) - { - priv->search_entry_changed_id = - g_signal_connect (priv->search_entry, "changed", - G_CALLBACK (gtk_tree_view_search_init), - tree_view); - } - - if (GTK_IS_ENTRY (entry)) - controller = gtk_entry_get_key_controller (GTK_ENTRY (entry)); - else - controller = gtk_search_entry_get_key_controller (GTK_SEARCH_ENTRY (entry)); - g_signal_connect (controller, "key-pressed", - G_CALLBACK (gtk_tree_view_search_key_pressed), tree_view); - - gtk_tree_view_search_init (priv->search_entry, tree_view); - } - else - { - priv->search_entry = NULL; - priv->search_custom_entry_set = FALSE; - } -} - -static void -gtk_tree_view_search_popover_hide (GtkWidget *search_popover, - GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - if (priv->disable_popdown) - return; - - if (priv->search_entry_changed_id) - { - g_signal_handler_disconnect (priv->search_entry, - priv->search_entry_changed_id); - priv->search_entry_changed_id = 0; - } - if (priv->typeselect_flush_timeout) - { - g_source_remove (priv->typeselect_flush_timeout); - priv->typeselect_flush_timeout = 0; - } - - if (gtk_widget_get_visible (search_popover)) - { - gtk_popover_popdown (GTK_POPOVER (search_popover)); - gtk_editable_set_text (GTK_EDITABLE (priv->search_entry), ""); - gtk_widget_grab_focus (GTK_WIDGET (tree_view)); - } -} - -/* Because we're visible but offscreen, we just set a flag in the preedit - * callback. - */ -static void -gtk_tree_view_search_preedit_changed (GtkText *text, - const char *predit, - GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - priv->imcontext_changed = 1; - if (priv->typeselect_flush_timeout) - { - g_source_remove (priv->typeselect_flush_timeout); - priv->typeselect_flush_timeout = - g_timeout_add (GTK_TREE_VIEW_SEARCH_DIALOG_TIMEOUT, - (GSourceFunc) gtk_tree_view_search_entry_flush_timeout, - tree_view); - gdk_source_set_static_name_by_id (priv->typeselect_flush_timeout, "[gtk] gtk_tree_view_search_entry_flush_timeout"); - } - -} - -static void -gtk_tree_view_search_changed (GtkEditable *editable, - GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - priv->imcontext_changed = 1; -} - -static void -gtk_tree_view_search_activate (GtkEntry *entry, - GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreePath *path; - - gtk_tree_view_search_popover_hide (priv->search_popover, tree_view); - - /* If we have a row selected and it's the cursor row, we activate - * the row XXX */ - if (priv->cursor_node && - GTK_TREE_RBNODE_FLAG_SET (priv->cursor_node, GTK_TREE_RBNODE_IS_SELECTED)) - { - path = _gtk_tree_path_new_from_rbtree (priv->cursor_tree, - priv->cursor_node); - - gtk_tree_view_row_activated (tree_view, path, priv->focus_column); - - gtk_tree_path_free (path); - } -} - -static void -gtk_tree_view_search_pressed_cb (GtkGesture *gesture, - int n_press, - double x, - double y, - GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - gtk_tree_view_search_popover_hide (priv->search_popover, tree_view); -} - -static gboolean -gtk_tree_view_search_scroll_event (GtkWidget *widget, - double dx, - double dy, - GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GdkScrollDirection direction; - - direction = dy > 0 ? GDK_SCROLL_DOWN : GDK_SCROLL_UP; - - if (direction == GDK_SCROLL_UP) - gtk_tree_view_search_move (widget, tree_view, TRUE); - else if (direction == GDK_SCROLL_DOWN) - gtk_tree_view_search_move (widget, tree_view, FALSE); - - /* renew the flush timeout */ - if (priv->typeselect_flush_timeout && - !priv->search_custom_entry_set) - { - g_source_remove (priv->typeselect_flush_timeout); - priv->typeselect_flush_timeout = - g_timeout_add (GTK_TREE_VIEW_SEARCH_DIALOG_TIMEOUT, - (GSourceFunc) gtk_tree_view_search_entry_flush_timeout, - tree_view); - gdk_source_set_static_name_by_id (priv->typeselect_flush_timeout, "[gtk] gtk_tree_view_search_entry_flush_timeout"); - } - - return GDK_EVENT_STOP; -} - -static gboolean -gtk_tree_view_search_key_pressed (GtkEventControllerKey *key, - guint keyval, - guint keycode, - GdkModifierType state, - GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkWidget *widget = priv->search_entry; - GdkModifierType default_accel; - gboolean retval = FALSE; - - g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); - - /* close window and cancel the search */ - if (!priv->search_custom_entry_set - && gtk_tree_view_search_key_cancels_search (keyval)) - { - gtk_tree_view_search_popover_hide (priv->search_popover, tree_view); - return TRUE; - } - - default_accel = GDK_CONTROL_MASK; - - /* select previous matching iter */ - if (keyval == GDK_KEY_Up || keyval == GDK_KEY_KP_Up) - { - if (!gtk_tree_view_search_move (widget, tree_view, TRUE)) - gtk_widget_error_bell (widget); - - retval = TRUE; - } - - if (((state & (default_accel | GDK_SHIFT_MASK)) == (default_accel | GDK_SHIFT_MASK)) - && (keyval == GDK_KEY_g || keyval == GDK_KEY_G)) - { - if (!gtk_tree_view_search_move (widget, tree_view, TRUE)) - gtk_widget_error_bell (widget); - - retval = TRUE; - } - - /* select next matching iter */ - if (keyval == GDK_KEY_Down || keyval == GDK_KEY_KP_Down) - { - if (!gtk_tree_view_search_move (widget, tree_view, FALSE)) - gtk_widget_error_bell (widget); - - retval = TRUE; - } - - if (((state & (default_accel | GDK_SHIFT_MASK)) == default_accel) - && (keyval == GDK_KEY_g || keyval == GDK_KEY_G)) - { - if (!gtk_tree_view_search_move (widget, tree_view, FALSE)) - gtk_widget_error_bell (widget); - - retval = TRUE; - } - - /* renew the flush timeout */ - if (retval && priv->typeselect_flush_timeout - && !priv->search_custom_entry_set) - { - g_source_remove (priv->typeselect_flush_timeout); - priv->typeselect_flush_timeout = - g_timeout_add (GTK_TREE_VIEW_SEARCH_DIALOG_TIMEOUT, - (GSourceFunc) gtk_tree_view_search_entry_flush_timeout, - tree_view); - gdk_source_set_static_name_by_id (priv->typeselect_flush_timeout, "[gtk] gtk_tree_view_search_entry_flush_timeout"); - } - - if (!retval) - gtk_event_controller_key_forward (key, priv->search_entry); - - return retval; -} - -/* this function returns FALSE if there is a search string but - * nothing was found, and TRUE otherwise. - */ -static gboolean -gtk_tree_view_search_move (GtkWidget *popover, - GtkTreeView *tree_view, - gboolean up) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - gboolean ret; - int len; - int count = 0; - const char *text; - GtkTreeIter iter; - GtkTreeModel *model; - GtkTreeSelection *selection; - - text = gtk_editable_get_text (GTK_EDITABLE (priv->search_entry)); - - g_return_val_if_fail (text != NULL, FALSE); - - len = strlen (text); - - if (up && priv->selected_iter == 1) - return len < 1; - - if (len < 1) - return TRUE; - - model = gtk_tree_view_get_model (tree_view); - selection = gtk_tree_view_get_selection (tree_view); - - /* search */ - gtk_tree_selection_unselect_all (selection); - if (!gtk_tree_model_get_iter_first (model, &iter)) - return TRUE; - - ret = gtk_tree_view_search_iter (model, selection, &iter, text, - &count, up?((priv->selected_iter) - 1):((priv->selected_iter + 1))); - - if (ret) - { - /* found */ - priv->selected_iter += up?(-1):(1); - return TRUE; - } - else - { - /* return to old iter */ - count = 0; - gtk_tree_model_get_iter_first (model, &iter); - gtk_tree_view_search_iter (model, selection, - &iter, text, - &count, priv->selected_iter); - return FALSE; - } -} - -static gboolean -gtk_tree_view_search_equal_func (GtkTreeModel *model, - int column, - const char *key, - GtkTreeIter *iter, - gpointer search_data) -{ - gboolean retval = TRUE; - const char *str; - char *normalized_string; - char *normalized_key; - char *case_normalized_string = NULL; - char *case_normalized_key = NULL; - GValue value = G_VALUE_INIT; - GValue transformed = G_VALUE_INIT; - - gtk_tree_model_get_value (model, iter, column, &value); - - g_value_init (&transformed, G_TYPE_STRING); - - if (!g_value_transform (&value, &transformed)) - { - g_value_unset (&value); - return TRUE; - } - - g_value_unset (&value); - - str = g_value_get_string (&transformed); - if (!str) - { - g_value_unset (&transformed); - return TRUE; - } - - normalized_string = g_utf8_normalize (str, -1, G_NORMALIZE_ALL); - normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL); - - if (normalized_string && normalized_key) - { - case_normalized_string = g_utf8_casefold (normalized_string, -1); - case_normalized_key = g_utf8_casefold (normalized_key, -1); - - if (strncmp (case_normalized_key, case_normalized_string, strlen (case_normalized_key)) == 0) - retval = FALSE; - } - - g_value_unset (&transformed); - g_free (normalized_key); - g_free (normalized_string); - g_free (case_normalized_key); - g_free (case_normalized_string); - - return retval; -} - -static gboolean -gtk_tree_view_search_iter (GtkTreeModel *model, - GtkTreeSelection *selection, - GtkTreeIter *iter, - const char *text, - int *count, - int n) -{ - GtkTreeRBTree *tree = NULL; - GtkTreeRBNode *node = NULL; - GtkTreePath *path; - - GtkTreeView *tree_view = gtk_tree_selection_get_tree_view (selection); - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - path = gtk_tree_model_get_path (model, iter); - _gtk_tree_view_find_node (tree_view, path, &tree, &node); - - do - { - if (! priv->search_equal_func (model, priv->search_column, text, iter, priv->search_user_data)) - { - (*count)++; - if (*count == n) - { - gtk_tree_view_scroll_to_cell (tree_view, path, NULL, - TRUE, 0.5, 0.0); - gtk_tree_selection_select_iter (selection, iter); - gtk_tree_view_real_set_cursor (tree_view, path, CLAMP_NODE); - - if (path) - gtk_tree_path_free (path); - - return TRUE; - } - } - - if (node->children) - { - gboolean has_child; - GtkTreeIter tmp; - - tree = node->children; - node = gtk_tree_rbtree_first (tree); - - tmp = *iter; - has_child = gtk_tree_model_iter_children (model, iter, &tmp); - gtk_tree_path_down (path); - - /* sanity check */ - TREE_VIEW_INTERNAL_ASSERT (has_child, FALSE); - } - else - { - gboolean done = FALSE; - - do - { - node = gtk_tree_rbtree_next (tree, node); - - if (node) - { - gboolean has_next; - - has_next = gtk_tree_model_iter_next (model, iter); - - done = TRUE; - gtk_tree_path_next (path); - - /* sanity check */ - TREE_VIEW_INTERNAL_ASSERT (has_next, FALSE); - } - else - { - gboolean has_parent; - GtkTreeIter tmp_iter = *iter; - - node = tree->parent_node; - tree = tree->parent_tree; - - if (!tree) - { - if (path) - gtk_tree_path_free (path); - - /* we've run out of tree, done with this func */ - return FALSE; - } - - has_parent = gtk_tree_model_iter_parent (model, - iter, - &tmp_iter); - gtk_tree_path_up (path); - - /* sanity check */ - TREE_VIEW_INTERNAL_ASSERT (has_parent, FALSE); - } - } - while (!done); - } - } - while (1); - - return FALSE; -} - -static void -gtk_tree_view_search_init (GtkWidget *entry, - GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - int ret; - int count = 0; - const char *text; - GtkTreeIter iter; - GtkTreeModel *model; - GtkTreeSelection *selection; - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - - text = gtk_editable_get_text (GTK_EDITABLE (entry)); - - model = gtk_tree_view_get_model (tree_view); - selection = gtk_tree_view_get_selection (tree_view); - - /* search */ - gtk_tree_selection_unselect_all (selection); - if (priv->typeselect_flush_timeout - && !priv->search_custom_entry_set) - { - g_source_remove (priv->typeselect_flush_timeout); - priv->typeselect_flush_timeout = - g_timeout_add (GTK_TREE_VIEW_SEARCH_DIALOG_TIMEOUT, - (GSourceFunc) gtk_tree_view_search_entry_flush_timeout, - tree_view); - gdk_source_set_static_name_by_id (priv->typeselect_flush_timeout, "[gtk] gtk_tree_view_search_entry_flush_timeout"); - } - - if (*text == '\0') - return; - - if (!gtk_tree_model_get_iter_first (model, &iter)) - return; - - ret = gtk_tree_view_search_iter (model, selection, - &iter, text, - &count, 1); - - if (ret) - priv->selected_iter = 1; -} - -void -_gtk_tree_view_remove_editable (GtkTreeView *tree_view, - GtkTreeViewColumn *column, - GtkCellEditable *cell_editable) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - if (priv->edited_column == NULL) - return; - - g_return_if_fail (column == priv->edited_column); - - priv->edited_column = NULL; - - if (gtk_widget_has_focus (GTK_WIDGET (cell_editable))) - gtk_widget_grab_focus (GTK_WIDGET (tree_view)); - - gtk_tree_view_remove (tree_view, GTK_WIDGET (cell_editable)); - - /* FIXME should only redraw a single node */ - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); -} - -static gboolean -gtk_tree_view_start_editing (GtkTreeView *tree_view, - GtkTreePath *cursor_path, - gboolean edit_only) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreeIter iter; - GdkRectangle cell_area; - GtkTreeViewColumn *focus_column; - guint flags = 0; /* can be 0, as the flags are primarily for rendering */ - int retval = FALSE; - GtkTreeRBTree *cursor_tree; - GtkTreeRBNode *cursor_node; - - g_assert (priv->focus_column); - focus_column = priv->focus_column; - - if (!gtk_widget_get_realized (GTK_WIDGET (tree_view))) - return FALSE; - - if (_gtk_tree_view_find_node (tree_view, cursor_path, &cursor_tree, &cursor_node) || - cursor_node == NULL) - return FALSE; - - gtk_tree_model_get_iter (priv->model, &iter, cursor_path); - - validate_row (tree_view, cursor_tree, cursor_node, &iter, cursor_path); - - gtk_tree_view_column_cell_set_cell_data (focus_column, - priv->model, - &iter, - GTK_TREE_RBNODE_FLAG_SET (cursor_node, GTK_TREE_RBNODE_IS_PARENT), - cursor_node->children ? TRUE : FALSE); - gtk_tree_view_get_cell_area (tree_view, - cursor_path, - focus_column, - &cell_area); - - if (gtk_cell_area_activate (gtk_cell_layout_get_area (GTK_CELL_LAYOUT (focus_column)), - _gtk_tree_view_column_get_context (focus_column), - GTK_WIDGET (tree_view), - &cell_area, - flags, edit_only)) - retval = TRUE; - - return retval; -} - -void -_gtk_tree_view_add_editable (GtkTreeView *tree_view, - GtkTreeViewColumn *column, - GtkTreePath *path, - GtkCellEditable *cell_editable, - GdkRectangle *cell_area) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GdkRectangle full_area; - GtkBorder border; - - priv->edited_column = column; - - gtk_tree_view_real_set_cursor (tree_view, path, CLAMP_NODE); - - priv->draw_keyfocus = TRUE; - - gtk_tree_view_get_cell_area (tree_view, path, column, &full_area); - border.left = cell_area->x - full_area.x; - border.top = cell_area->y - full_area.y; - border.right = (full_area.x + full_area.width) - (cell_area->x + cell_area->width); - border.bottom = (full_area.y + full_area.height) - (cell_area->y + cell_area->height); - - gtk_tree_view_put (tree_view, - GTK_WIDGET (cell_editable), - path, - column, - &border); -} - -static void -gtk_tree_view_stop_editing (GtkTreeView *tree_view, - gboolean cancel_editing) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreeViewColumn *column; - - if (priv->edited_column == NULL) - return; - - /* - * This is very evil. We need to do this, because - * gtk_cell_editable_editing_done may trigger gtk_tree_view_row_changed - * later on. If gtk_tree_view_row_changed notices - * priv->edited_column != NULL, it'll call - * gtk_tree_view_stop_editing again. Bad things will happen then. - * - * Please read that again if you intend to modify anything here. - */ - - column = priv->edited_column; - gtk_cell_area_stop_editing (gtk_cell_layout_get_area (GTK_CELL_LAYOUT (column)), cancel_editing); - priv->edited_column = NULL; -} - - -/** - * gtk_tree_view_set_hover_selection: - * @tree_view: a `GtkTreeView` - * @hover: %TRUE to enable hover selection mode - * - * Enables or disables the hover selection mode of @tree_view. - * Hover selection makes the selected row follow the pointer. - * Currently, this works only for the selection modes - * %GTK_SELECTION_SINGLE and %GTK_SELECTION_BROWSE. - **/ -void -gtk_tree_view_set_hover_selection (GtkTreeView *tree_view, - gboolean hover) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - hover = hover != FALSE; - - if (hover != priv->hover_selection) - { - priv->hover_selection = hover; - - g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_HOVER_SELECTION]); - } -} - -/** - * gtk_tree_view_get_hover_selection: - * @tree_view: a `GtkTreeView` - * - * Returns whether hover selection mode is turned on for @tree_view. - * - * Returns: %TRUE if @tree_view is in hover selection mode - **/ -gboolean -gtk_tree_view_get_hover_selection (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); - - return priv->hover_selection; -} - -/** - * gtk_tree_view_set_hover_expand: - * @tree_view: a `GtkTreeView` - * @expand: %TRUE to enable hover selection mode - * - * Enables or disables the hover expansion mode of @tree_view. - * Hover expansion makes rows expand or collapse if the pointer - * moves over them. - **/ -void -gtk_tree_view_set_hover_expand (GtkTreeView *tree_view, - gboolean expand) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - expand = expand != FALSE; - - if (expand != priv->hover_expand) - { - priv->hover_expand = expand; - - g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_HOVER_EXPAND]); - } -} - -/** - * gtk_tree_view_get_hover_expand: - * @tree_view: a `GtkTreeView` - * - * Returns whether hover expansion mode is turned on for @tree_view. - * - * Returns: %TRUE if @tree_view is in hover expansion mode - **/ -gboolean -gtk_tree_view_get_hover_expand (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); - - return priv->hover_expand; -} - -/** - * gtk_tree_view_set_rubber_banding: - * @tree_view: a `GtkTreeView` - * @enable: %TRUE to enable rubber banding - * - * Enables or disables rubber banding in @tree_view. If the selection mode - * is %GTK_SELECTION_MULTIPLE, rubber banding will allow the user to select - * multiple rows by dragging the mouse. - **/ -void -gtk_tree_view_set_rubber_banding (GtkTreeView *tree_view, - gboolean enable) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - enable = enable != FALSE; - - if (enable != priv->rubber_banding_enable) - { - priv->rubber_banding_enable = enable; - - g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_RUBBER_BANDING]); - } -} - -/** - * gtk_tree_view_get_rubber_banding: - * @tree_view: a `GtkTreeView` - * - * Returns whether rubber banding is turned on for @tree_view. If the - * selection mode is %GTK_SELECTION_MULTIPLE, rubber banding will allow the - * user to select multiple rows by dragging the mouse. - * - * Returns: %TRUE if rubber banding in @tree_view is enabled. - **/ -gboolean -gtk_tree_view_get_rubber_banding (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - return priv->rubber_banding_enable; -} - -/** - * gtk_tree_view_is_rubber_banding_active: - * @tree_view: a `GtkTreeView` - * - * Returns whether a rubber banding operation is currently being done - * in @tree_view. - * - * Returns: %TRUE if a rubber banding operation is currently being - * done in @tree_view. - **/ -gboolean -gtk_tree_view_is_rubber_banding_active (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); - - if (priv->rubber_banding_enable - && priv->rubber_band_status == RUBBER_BAND_ACTIVE) - return TRUE; - - return FALSE; -} - -/** - * gtk_tree_view_get_row_separator_func: (skip) - * @tree_view: a `GtkTreeView` - * - * Returns the current row separator function. - * - * Returns: the current row separator function. - **/ -GtkTreeViewRowSeparatorFunc -gtk_tree_view_get_row_separator_func (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL); - - return priv->row_separator_func; -} - -/** - * gtk_tree_view_set_row_separator_func: - * @tree_view: a `GtkTreeView` - * @func: (nullable): a `GtkTreeView`RowSeparatorFunc - * @data: (nullable): user data to pass to @func - * @destroy: (nullable): destroy notifier for @data - * - * Sets the row separator function, which is used to determine - * whether a row should be drawn as a separator. If the row separator - * function is %NULL, no separators are drawn. This is the default value. - **/ -void -gtk_tree_view_set_row_separator_func (GtkTreeView *tree_view, - GtkTreeViewRowSeparatorFunc func, - gpointer data, - GDestroyNotify destroy) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - - if (priv->row_separator_destroy) - priv->row_separator_destroy (priv->row_separator_data); - - priv->row_separator_func = func; - priv->row_separator_data = data; - priv->row_separator_destroy = destroy; - - /* Have the tree recalculate heights */ - gtk_tree_rbtree_mark_invalid (priv->tree); - gtk_widget_queue_resize (GTK_WIDGET (tree_view)); -} - -/** - * gtk_tree_view_get_grid_lines: - * @tree_view: a `GtkTreeView` - * - * Returns which grid lines are enabled in @tree_view. - * - * Returns: a `GtkTreeView`GridLines value indicating which grid lines - * are enabled. - */ -GtkTreeViewGridLines -gtk_tree_view_get_grid_lines (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), 0); - - return priv->grid_lines; -} - -/** - * gtk_tree_view_set_grid_lines: - * @tree_view: a `GtkTreeView` - * @grid_lines: a `GtkTreeView`GridLines value indicating which grid lines to - * enable. - * - * Sets which grid lines to draw in @tree_view. - */ -void -gtk_tree_view_set_grid_lines (GtkTreeView *tree_view, - GtkTreeViewGridLines grid_lines) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GtkTreeViewGridLines old_grid_lines; - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - - old_grid_lines = priv->grid_lines; - priv->grid_lines = grid_lines; - - if (old_grid_lines != grid_lines) - { - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - - g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_ENABLE_GRID_LINES]); - } -} - -/** - * gtk_tree_view_get_enable_tree_lines: - * @tree_view: a `GtkTreeView`. - * - * Returns whether or not tree lines are drawn in @tree_view. - * - * Returns: %TRUE if tree lines are drawn in @tree_view, %FALSE - * otherwise. - */ -gboolean -gtk_tree_view_get_enable_tree_lines (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); - - return priv->tree_lines_enabled; -} - -/** - * gtk_tree_view_set_enable_tree_lines: - * @tree_view: a `GtkTreeView` - * @enabled: %TRUE to enable tree line drawing, %FALSE otherwise. - * - * Sets whether to draw lines interconnecting the expanders in @tree_view. - * This does not have any visible effects for lists. - */ -void -gtk_tree_view_set_enable_tree_lines (GtkTreeView *tree_view, - gboolean enabled) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - gboolean was_enabled; - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - - enabled = enabled != FALSE; - - was_enabled = priv->tree_lines_enabled; - - priv->tree_lines_enabled = enabled; - - if (was_enabled != enabled) - { - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - - g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_ENABLE_TREE_LINES]); - } -} - - -/** - * gtk_tree_view_set_show_expanders: - * @tree_view: a `GtkTreeView` - * @enabled: %TRUE to enable expander drawing, %FALSE otherwise. - * - * Sets whether to draw and enable expanders and indent child rows in - * @tree_view. When disabled there will be no expanders visible in trees - * and there will be no way to expand and collapse rows by default. Also - * note that hiding the expanders will disable the default indentation. You - * can set a custom indentation in this case using - * gtk_tree_view_set_level_indentation(). - * This does not have any visible effects for lists. - */ -void -gtk_tree_view_set_show_expanders (GtkTreeView *tree_view, - gboolean enabled) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - - enabled = enabled != FALSE; - if (priv->show_expanders != enabled) - { - priv->show_expanders = enabled; - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_SHOW_EXPANDERS]); - } -} - -/** - * gtk_tree_view_get_show_expanders: - * @tree_view: a `GtkTreeView`. - * - * Returns whether or not expanders are drawn in @tree_view. - * - * Returns: %TRUE if expanders are drawn in @tree_view, %FALSE - * otherwise. - */ -gboolean -gtk_tree_view_get_show_expanders (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); - - return priv->show_expanders; -} - -/** - * gtk_tree_view_set_level_indentation: - * @tree_view: a `GtkTreeView` - * @indentation: the amount, in pixels, of extra indentation in @tree_view. - * - * Sets the amount of extra indentation for child levels to use in @tree_view - * in addition to the default indentation. The value should be specified in - * pixels, a value of 0 disables this feature and in this case only the default - * indentation will be used. - * This does not have any visible effects for lists. - */ -void -gtk_tree_view_set_level_indentation (GtkTreeView *tree_view, - int indentation) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - priv->level_indentation = indentation; - - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); -} - -/** - * gtk_tree_view_get_level_indentation: - * @tree_view: a `GtkTreeView`. - * - * Returns the amount, in pixels, of extra indentation for child levels - * in @tree_view. - * - * Returns: the amount of extra indentation for child levels in - * @tree_view. A return value of 0 means that this feature is disabled. - */ -int -gtk_tree_view_get_level_indentation (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), 0); - - return priv->level_indentation; -} - -/** - * gtk_tree_view_set_tooltip_row: - * @tree_view: a `GtkTreeView` - * @tooltip: a `GtkTooltip` - * @path: a `GtkTreePath` - * - * Sets the tip area of @tooltip to be the area covered by the row at @path. - * See also gtk_tree_view_set_tooltip_column() for a simpler alternative. - * See also gtk_tooltip_set_tip_area(). - */ -void -gtk_tree_view_set_tooltip_row (GtkTreeView *tree_view, - GtkTooltip *tooltip, - GtkTreePath *path) -{ - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - g_return_if_fail (GTK_IS_TOOLTIP (tooltip)); - - gtk_tree_view_set_tooltip_cell (tree_view, tooltip, path, NULL, NULL); -} - -/** - * gtk_tree_view_set_tooltip_cell: - * @tree_view: a `GtkTreeView` - * @tooltip: a `GtkTooltip` - * @path: (nullable): a `GtkTreePath` - * @column: (nullable): a `GtkTreeViewColumn` - * @cell: (nullable): a `GtkCellRenderer` - * - * Sets the tip area of @tooltip to the area @path, @column and @cell have - * in common. For example if @path is %NULL and @column is set, the tip - * area will be set to the full area covered by @column. See also - * gtk_tooltip_set_tip_area(). - * - * Note that if @path is not specified and @cell is set and part of a column - * containing the expander, the tooltip might not show and hide at the correct - * position. In such cases @path must be set to the current node under the - * mouse cursor for this function to operate correctly. - * - * See also gtk_tree_view_set_tooltip_column() for a simpler alternative. - */ -void -gtk_tree_view_set_tooltip_cell (GtkTreeView *tree_view, - GtkTooltip *tooltip, - GtkTreePath *path, - GtkTreeViewColumn *column, - GtkCellRenderer *cell) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - GdkRectangle rect; - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - g_return_if_fail (GTK_IS_TOOLTIP (tooltip)); - g_return_if_fail (column == NULL || GTK_IS_TREE_VIEW_COLUMN (column)); - g_return_if_fail (cell == NULL || GTK_IS_CELL_RENDERER (cell)); - - /* Determine x values. */ - if (column && cell) - { - GdkRectangle tmp; - int start, width; - - /* We always pass in path here, whether it is NULL or not. - * For cells in expander columns path must be specified so that - * we can correctly account for the indentation. This also means - * that the tooltip is constrained vertically by the "Determine y - * values" code below; this is not a real problem since cells actually - * don't stretch vertically in contrast to columns. - */ - gtk_tree_view_get_cell_area (tree_view, path, column, &tmp); - gtk_tree_view_column_cell_get_position (column, cell, &start, &width); - - gtk_tree_view_convert_bin_window_to_widget_coords (tree_view, - tmp.x + start, 0, - &rect.x, NULL); - rect.width = width; - } - else if (column) - { - GdkRectangle tmp; - - gtk_tree_view_get_background_area (tree_view, NULL, column, &tmp); - gtk_tree_view_convert_bin_window_to_widget_coords (tree_view, - tmp.x, 0, - &rect.x, NULL); - rect.width = tmp.width; - } - else - { - rect.x = 0; - rect.width = gtk_widget_get_width (GTK_WIDGET (tree_view));; - } - - /* Determine y values. */ - if (path) - { - GdkRectangle tmp; - - gtk_tree_view_get_background_area (tree_view, path, NULL, &tmp); - gtk_tree_view_convert_bin_window_to_widget_coords (tree_view, - 0, tmp.y, - NULL, &rect.y); - rect.height = tmp.height; - } - else - { - rect.y = 0; - rect.height = gtk_adjustment_get_page_size (priv->vadjustment); - } - - gtk_tooltip_set_tip_area (tooltip, &rect); -} - -/** - * gtk_tree_view_get_tooltip_context: - * @tree_view: a `GtkTreeView` - * @x: the x coordinate (relative to widget coordinates) - * @y: the y coordinate (relative to widget coordinates) - * @keyboard_tip: whether this is a keyboard tooltip or not - * @model: (out) (optional) (nullable) (transfer none): a pointer to - * receive a `GtkTreeModel` - * @path: (out) (optional): a pointer to receive a `GtkTreePath` - * @iter: (out) (optional): a pointer to receive a `GtkTreeIter` - * - * This function is supposed to be used in a ::query-tooltip - * signal handler for `GtkTreeView`. The @x, @y and @keyboard_tip values - * which are received in the signal handler, should be passed to this - * function without modification. - * - * The return value indicates whether there is a tree view row at the given - * coordinates (%TRUE) or not (%FALSE) for mouse tooltips. For keyboard - * tooltips the row returned will be the cursor row. When %TRUE, then any of - * @model, @path and @iter which have been provided will be set to point to - * that row and the corresponding model. @x and @y will always be converted - * to be relative to @tree_view’s bin_window if @keyboard_tooltip is %FALSE. - * - * Returns: whether or not the given tooltip context points to a row - */ -gboolean -gtk_tree_view_get_tooltip_context (GtkTreeView *tree_view, - int x, - int y, - gboolean keyboard_tip, - GtkTreeModel **model, - GtkTreePath **path, - GtkTreeIter *iter) -{ - GtkTreePath *tmppath = NULL; - - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); - - if (keyboard_tip) - { - gtk_tree_view_get_cursor (tree_view, &tmppath, NULL); - - if (!tmppath) - return FALSE; - } - else - { - int rel_x, rel_y; - - gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, x, y, - &rel_x, &rel_y); - - if (!gtk_tree_view_get_path_at_pos (tree_view, rel_x, rel_y, - &tmppath, NULL, NULL, NULL)) - return FALSE; - } - - if (model) - *model = gtk_tree_view_get_model (tree_view); - - if (iter) - gtk_tree_model_get_iter (gtk_tree_view_get_model (tree_view), - iter, tmppath); - - if (path) - *path = tmppath; - else - gtk_tree_path_free (tmppath); - - return TRUE; -} - -static gboolean -gtk_tree_view_set_tooltip_query_cb (GtkWidget *widget, - int x, - int y, - gboolean keyboard_tip, - GtkTooltip *tooltip, - gpointer data) -{ - GValue value = G_VALUE_INIT; - GValue transformed = G_VALUE_INIT; - GtkTreeIter iter; - GtkTreePath *path; - GtkTreeModel *model; - GtkTreeView *tree_view = GTK_TREE_VIEW (widget); - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - if (!gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (widget), - x, y, - keyboard_tip, - &model, &path, &iter)) - return FALSE; - - gtk_tree_model_get_value (model, &iter, - priv->tooltip_column, &value); - - g_value_init (&transformed, G_TYPE_STRING); - - if (!g_value_transform (&value, &transformed)) - { - g_value_unset (&value); - gtk_tree_path_free (path); - - return FALSE; - } - - g_value_unset (&value); - - if (!g_value_get_string (&transformed)) - { - g_value_unset (&transformed); - gtk_tree_path_free (path); - - return FALSE; - } - - gtk_tooltip_set_markup (tooltip, g_value_get_string (&transformed)); - gtk_tree_view_set_tooltip_row (tree_view, tooltip, path); - - gtk_tree_path_free (path); - g_value_unset (&transformed); - - return TRUE; -} - -/** - * gtk_tree_view_set_tooltip_column: - * @tree_view: a `GtkTreeView` - * @column: an integer, which is a valid column number for @tree_view’s model - * - * If you only plan to have simple (text-only) tooltips on full rows, you - * can use this function to have `GtkTreeView` handle these automatically - * for you. @column should be set to the column in @tree_view’s model - * containing the tooltip texts, or -1 to disable this feature. - * - * When enabled, `GtkWidget:has-tooltip` will be set to %TRUE and - * @tree_view will connect a `GtkWidget::query-tooltip` signal handler. - * - * Note that the signal handler sets the text with gtk_tooltip_set_markup(), - * so &, <, etc have to be escaped in the text. - */ -void -gtk_tree_view_set_tooltip_column (GtkTreeView *tree_view, - int column) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - - if (column == priv->tooltip_column) - return; - - if (column == -1) - { - g_signal_handlers_disconnect_by_func (tree_view, - gtk_tree_view_set_tooltip_query_cb, - NULL); - gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), FALSE); - } - else - { - if (priv->tooltip_column == -1) - { - g_signal_connect (tree_view, "query-tooltip", - G_CALLBACK (gtk_tree_view_set_tooltip_query_cb), NULL); - gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), TRUE); - } - } - - priv->tooltip_column = column; - g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_TOOLTIP_COLUMN]); -} - -/** - * gtk_tree_view_get_tooltip_column: - * @tree_view: a `GtkTreeView` - * - * Returns the column of @tree_view’s model which is being used for - * displaying tooltips on @tree_view’s rows. - * - * Returns: the index of the tooltip column that is currently being - * used, or -1 if this is disabled. - */ -int -gtk_tree_view_get_tooltip_column (GtkTreeView *tree_view) -{ - GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view); - - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), 0); - - return priv->tooltip_column; -} - -static gboolean -gtk_tree_view_get_border (GtkScrollable *scrollable, - GtkBorder *border) -{ - border->top = gtk_tree_view_get_effective_header_height (GTK_TREE_VIEW (scrollable)); - - return TRUE; -} - -static void -gtk_tree_view_scrollable_init (GtkScrollableInterface *iface) -{ - iface->get_border = gtk_tree_view_get_border; -} diff --git a/gtk/gtktreeview.h b/gtk/gtktreeview.h deleted file mode 100644 index 806f3594e1..0000000000 --- a/gtk/gtktreeview.h +++ /dev/null @@ -1,548 +0,0 @@ -/* gtktreeview.h - * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#ifndef __GTK_TREE_VIEW_H__ -#define __GTK_TREE_VIEW_H__ - -#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) -#error "Only can be included directly." -#endif - -#include -#include -#include -#include - -G_BEGIN_DECLS - -/** - * GtkTreeViewDropPosition: - * @GTK_TREE_VIEW_DROP_BEFORE: dropped row is inserted before - * @GTK_TREE_VIEW_DROP_AFTER: dropped row is inserted after - * @GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: dropped row becomes a child or is inserted before - * @GTK_TREE_VIEW_DROP_INTO_OR_AFTER: dropped row becomes a child or is inserted after - * - * An enum for determining where a dropped row goes. - */ -typedef enum -{ - /* drop before/after this row */ - GTK_TREE_VIEW_DROP_BEFORE, - GTK_TREE_VIEW_DROP_AFTER, - /* drop as a child of this row (with fallback to before or after - * if into is not possible) - */ - GTK_TREE_VIEW_DROP_INTO_OR_BEFORE, - GTK_TREE_VIEW_DROP_INTO_OR_AFTER -} GtkTreeViewDropPosition; - -#define GTK_TYPE_TREE_VIEW (gtk_tree_view_get_type ()) -#define GTK_TREE_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_TREE_VIEW, GtkTreeView)) -#define GTK_IS_TREE_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_TREE_VIEW)) -#define GTK_TREE_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_TREE_VIEW, GtkTreeViewClass)) -#define GTK_IS_TREE_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_TREE_VIEW)) -#define GTK_TREE_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_TREE_VIEW, GtkTreeViewClass)) - -typedef struct _GtkTreeView GtkTreeView; -typedef struct _GtkTreeViewClass GtkTreeViewClass; -typedef struct _GtkTreeSelection GtkTreeSelection; - -/** - * GtkTreeViewColumnDropFunc: - * @tree_view: A `GtkTreeView` - * @column: The `GtkTreeViewColumn` being dragged - * @prev_column: A `GtkTreeViewColumn` on one side of @column - * @next_column: A `GtkTreeViewColumn` on the other side of @column - * @data: (closure): user data - * - * Function type for determining whether @column can be dropped in a - * particular spot (as determined by @prev_column and @next_column). In - * left to right locales, @prev_column is on the left of the potential drop - * spot, and @next_column is on the right. In right to left mode, this is - * reversed. This function should return %TRUE if the spot is a valid drop - * spot. Please note that returning %TRUE does not actually indicate that - * the column drop was made, but is meant only to indicate a possible drop - * spot to the user. - * - * Returns: %TRUE, if @column can be dropped in this spot - */ -typedef gboolean (* GtkTreeViewColumnDropFunc) (GtkTreeView *tree_view, - GtkTreeViewColumn *column, - GtkTreeViewColumn *prev_column, - GtkTreeViewColumn *next_column, - gpointer data); - -/** - * GtkTreeViewMappingFunc: - * @tree_view: A `GtkTreeView` - * @path: The path that’s expanded - * @user_data: user data - * - * Function used for gtk_tree_view_map_expanded_rows(). - */ -typedef void (* GtkTreeViewMappingFunc) (GtkTreeView *tree_view, - GtkTreePath *path, - gpointer user_data); - -/** - * GtkTreeViewSearchEqualFunc: - * @model: the `GtkTreeModel` being searched - * @column: the search column set by gtk_tree_view_set_search_column() - * @key: the key string to compare with - * @iter: a `GtkTreeIter` pointing the row of @model that should be compared - * with @key. - * @search_data: (closure): user data from gtk_tree_view_set_search_equal_func() - * - * A function used for checking whether a row in @model matches - * a search key string entered by the user. Note the return value - * is reversed from what you would normally expect, though it - * has some similarity to strcmp() returning 0 for equal strings. - * - * Returns: %FALSE if the row matches, %TRUE otherwise. - */ -typedef gboolean (*GtkTreeViewSearchEqualFunc) (GtkTreeModel *model, - int column, - const char *key, - GtkTreeIter *iter, - gpointer search_data); - -/** - * GtkTreeViewRowSeparatorFunc: - * @model: the `GtkTreeModel` - * @iter: a `GtkTreeIter` pointing at a row in @model - * @data: (closure): user data - * - * Function type for determining whether the row pointed to by @iter should - * be rendered as a separator. A common way to implement this is to have a - * boolean column in the model, whose values the `GtkTreeViewRowSeparatorFunc` - * returns. - * - * Returns: %TRUE if the row is a separator - */ -typedef gboolean (*GtkTreeViewRowSeparatorFunc) (GtkTreeModel *model, - GtkTreeIter *iter, - gpointer data); - -struct _GtkTreeView -{ - GtkWidget parent_instance; -}; - -struct _GtkTreeViewClass -{ - GtkWidgetClass parent_class; - - void (* row_activated) (GtkTreeView *tree_view, - GtkTreePath *path, - GtkTreeViewColumn *column); - gboolean (* test_expand_row) (GtkTreeView *tree_view, - GtkTreeIter *iter, - GtkTreePath *path); - gboolean (* test_collapse_row) (GtkTreeView *tree_view, - GtkTreeIter *iter, - GtkTreePath *path); - void (* row_expanded) (GtkTreeView *tree_view, - GtkTreeIter *iter, - GtkTreePath *path); - void (* row_collapsed) (GtkTreeView *tree_view, - GtkTreeIter *iter, - GtkTreePath *path); - void (* columns_changed) (GtkTreeView *tree_view); - void (* cursor_changed) (GtkTreeView *tree_view); - - /* Key Binding signals */ - gboolean (* move_cursor) (GtkTreeView *tree_view, - GtkMovementStep step, - int count, - gboolean extend, - gboolean modify); - gboolean (* select_all) (GtkTreeView *tree_view); - gboolean (* unselect_all) (GtkTreeView *tree_view); - gboolean (* select_cursor_row) (GtkTreeView *tree_view, - gboolean start_editing); - gboolean (* toggle_cursor_row) (GtkTreeView *tree_view); - gboolean (* expand_collapse_cursor_row) (GtkTreeView *tree_view, - gboolean logical, - gboolean expand, - gboolean open_all); - gboolean (* select_cursor_parent) (GtkTreeView *tree_view); - gboolean (* start_interactive_search) (GtkTreeView *tree_view); - - /*< private >*/ - gpointer _reserved[16]; -}; - -GDK_AVAILABLE_IN_ALL -GType gtk_tree_view_get_type (void) G_GNUC_CONST; - -/* Creators */ -GDK_AVAILABLE_IN_ALL -GtkWidget *gtk_tree_view_new (void); -GDK_AVAILABLE_IN_ALL -GtkWidget *gtk_tree_view_new_with_model (GtkTreeModel *model); - -/* Accessors */ -GDK_AVAILABLE_IN_ALL -GtkTreeModel *gtk_tree_view_get_model (GtkTreeView *tree_view); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_set_model (GtkTreeView *tree_view, - GtkTreeModel *model); -GDK_AVAILABLE_IN_ALL -GtkTreeSelection *gtk_tree_view_get_selection (GtkTreeView *tree_view); - -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_view_get_headers_visible (GtkTreeView *tree_view); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_set_headers_visible (GtkTreeView *tree_view, - gboolean headers_visible); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_columns_autosize (GtkTreeView *tree_view); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_view_get_headers_clickable (GtkTreeView *tree_view); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_set_headers_clickable (GtkTreeView *tree_view, - gboolean setting); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_view_get_activate_on_single_click (GtkTreeView *tree_view); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_set_activate_on_single_click (GtkTreeView *tree_view, - gboolean single); - -/* Column functions */ -GDK_AVAILABLE_IN_ALL -int gtk_tree_view_append_column (GtkTreeView *tree_view, - GtkTreeViewColumn *column); -GDK_AVAILABLE_IN_ALL -int gtk_tree_view_remove_column (GtkTreeView *tree_view, - GtkTreeViewColumn *column); -GDK_AVAILABLE_IN_ALL -int gtk_tree_view_insert_column (GtkTreeView *tree_view, - GtkTreeViewColumn *column, - int position); -GDK_AVAILABLE_IN_ALL -int gtk_tree_view_insert_column_with_attributes (GtkTreeView *tree_view, - int position, - const char *title, - GtkCellRenderer *cell, - ...) G_GNUC_NULL_TERMINATED; -GDK_AVAILABLE_IN_ALL -int gtk_tree_view_insert_column_with_data_func (GtkTreeView *tree_view, - int position, - const char *title, - GtkCellRenderer *cell, - GtkTreeCellDataFunc func, - gpointer data, - GDestroyNotify dnotify); - -GDK_AVAILABLE_IN_ALL -guint gtk_tree_view_get_n_columns (GtkTreeView *tree_view); -GDK_AVAILABLE_IN_ALL -GtkTreeViewColumn *gtk_tree_view_get_column (GtkTreeView *tree_view, - int n); -GDK_AVAILABLE_IN_ALL -GList *gtk_tree_view_get_columns (GtkTreeView *tree_view); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_move_column_after (GtkTreeView *tree_view, - GtkTreeViewColumn *column, - GtkTreeViewColumn *base_column); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_set_expander_column (GtkTreeView *tree_view, - GtkTreeViewColumn *column); -GDK_AVAILABLE_IN_ALL -GtkTreeViewColumn *gtk_tree_view_get_expander_column (GtkTreeView *tree_view); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_set_column_drag_function (GtkTreeView *tree_view, - GtkTreeViewColumnDropFunc func, - gpointer user_data, - GDestroyNotify destroy); - -/* Actions */ -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_scroll_to_point (GtkTreeView *tree_view, - int tree_x, - int tree_y); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_scroll_to_cell (GtkTreeView *tree_view, - GtkTreePath *path, - GtkTreeViewColumn *column, - gboolean use_align, - float row_align, - float col_align); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_row_activated (GtkTreeView *tree_view, - GtkTreePath *path, - GtkTreeViewColumn *column); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_expand_all (GtkTreeView *tree_view); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_collapse_all (GtkTreeView *tree_view); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_expand_to_path (GtkTreeView *tree_view, - GtkTreePath *path); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_view_expand_row (GtkTreeView *tree_view, - GtkTreePath *path, - gboolean open_all); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_view_collapse_row (GtkTreeView *tree_view, - GtkTreePath *path); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_map_expanded_rows (GtkTreeView *tree_view, - GtkTreeViewMappingFunc func, - gpointer data); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_view_row_expanded (GtkTreeView *tree_view, - GtkTreePath *path); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_set_reorderable (GtkTreeView *tree_view, - gboolean reorderable); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_view_get_reorderable (GtkTreeView *tree_view); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_set_cursor (GtkTreeView *tree_view, - GtkTreePath *path, - GtkTreeViewColumn *focus_column, - gboolean start_editing); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_set_cursor_on_cell (GtkTreeView *tree_view, - GtkTreePath *path, - GtkTreeViewColumn *focus_column, - GtkCellRenderer *focus_cell, - gboolean start_editing); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_get_cursor (GtkTreeView *tree_view, - GtkTreePath **path, - GtkTreeViewColumn **focus_column); - - -/* Layout information */ -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_view_get_path_at_pos (GtkTreeView *tree_view, - int x, - int y, - GtkTreePath **path, - GtkTreeViewColumn **column, - int *cell_x, - int *cell_y); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_get_cell_area (GtkTreeView *tree_view, - GtkTreePath *path, - GtkTreeViewColumn *column, - GdkRectangle *rect); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_get_background_area (GtkTreeView *tree_view, - GtkTreePath *path, - GtkTreeViewColumn *column, - GdkRectangle *rect); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_get_visible_rect (GtkTreeView *tree_view, - GdkRectangle *visible_rect); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_view_get_visible_range (GtkTreeView *tree_view, - GtkTreePath **start_path, - GtkTreePath **end_path); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_view_is_blank_at_pos (GtkTreeView *tree_view, - int x, - int y, - GtkTreePath **path, - GtkTreeViewColumn **column, - int *cell_x, - int *cell_y); - -/* Drag-and-Drop support */ -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_enable_model_drag_source (GtkTreeView *tree_view, - GdkModifierType start_button_mask, - GdkContentFormats *formats, - GdkDragAction actions); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_enable_model_drag_dest (GtkTreeView *tree_view, - GdkContentFormats *formats, - GdkDragAction actions); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_unset_rows_drag_source (GtkTreeView *tree_view); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_unset_rows_drag_dest (GtkTreeView *tree_view); - - -/* These are useful to implement your own custom stuff. */ -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_set_drag_dest_row (GtkTreeView *tree_view, - GtkTreePath *path, - GtkTreeViewDropPosition pos); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_get_drag_dest_row (GtkTreeView *tree_view, - GtkTreePath **path, - GtkTreeViewDropPosition *pos); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_view_get_dest_row_at_pos (GtkTreeView *tree_view, - int drag_x, - int drag_y, - GtkTreePath **path, - GtkTreeViewDropPosition *pos); -GDK_AVAILABLE_IN_ALL -GdkPaintable *gtk_tree_view_create_row_drag_icon (GtkTreeView *tree_view, - GtkTreePath *path); - -/* Interactive search */ -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_set_enable_search (GtkTreeView *tree_view, - gboolean enable_search); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_view_get_enable_search (GtkTreeView *tree_view); -GDK_AVAILABLE_IN_ALL -int gtk_tree_view_get_search_column (GtkTreeView *tree_view); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_set_search_column (GtkTreeView *tree_view, - int column); -GDK_AVAILABLE_IN_ALL -GtkTreeViewSearchEqualFunc gtk_tree_view_get_search_equal_func (GtkTreeView *tree_view); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_set_search_equal_func (GtkTreeView *tree_view, - GtkTreeViewSearchEqualFunc search_equal_func, - gpointer search_user_data, - GDestroyNotify search_destroy); - -GDK_AVAILABLE_IN_ALL -GtkEditable *gtk_tree_view_get_search_entry (GtkTreeView *tree_view); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_set_search_entry (GtkTreeView *tree_view, - GtkEditable *entry); - -/* Convert between the different coordinate systems */ -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_convert_widget_to_tree_coords (GtkTreeView *tree_view, - int wx, - int wy, - int *tx, - int *ty); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_convert_tree_to_widget_coords (GtkTreeView *tree_view, - int tx, - int ty, - int *wx, - int *wy); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_convert_widget_to_bin_window_coords (GtkTreeView *tree_view, - int wx, - int wy, - int *bx, - int *by); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_convert_bin_window_to_widget_coords (GtkTreeView *tree_view, - int bx, - int by, - int *wx, - int *wy); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_convert_tree_to_bin_window_coords (GtkTreeView *tree_view, - int tx, - int ty, - int *bx, - int *by); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_convert_bin_window_to_tree_coords (GtkTreeView *tree_view, - int bx, - int by, - int *tx, - int *ty); - -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_set_fixed_height_mode (GtkTreeView *tree_view, - gboolean enable); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_view_get_fixed_height_mode (GtkTreeView *tree_view); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_set_hover_selection (GtkTreeView *tree_view, - gboolean hover); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_view_get_hover_selection (GtkTreeView *tree_view); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_set_hover_expand (GtkTreeView *tree_view, - gboolean expand); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_view_get_hover_expand (GtkTreeView *tree_view); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_set_rubber_banding (GtkTreeView *tree_view, - gboolean enable); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_view_get_rubber_banding (GtkTreeView *tree_view); - -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_view_is_rubber_banding_active (GtkTreeView *tree_view); - -GDK_AVAILABLE_IN_ALL -GtkTreeViewRowSeparatorFunc gtk_tree_view_get_row_separator_func (GtkTreeView *tree_view); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_set_row_separator_func (GtkTreeView *tree_view, - GtkTreeViewRowSeparatorFunc func, - gpointer data, - GDestroyNotify destroy); - -GDK_AVAILABLE_IN_ALL -GtkTreeViewGridLines gtk_tree_view_get_grid_lines (GtkTreeView *tree_view); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_set_grid_lines (GtkTreeView *tree_view, - GtkTreeViewGridLines grid_lines); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_view_get_enable_tree_lines (GtkTreeView *tree_view); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_set_enable_tree_lines (GtkTreeView *tree_view, - gboolean enabled); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_set_show_expanders (GtkTreeView *tree_view, - gboolean enabled); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_view_get_show_expanders (GtkTreeView *tree_view); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_set_level_indentation (GtkTreeView *tree_view, - int indentation); -GDK_AVAILABLE_IN_ALL -int gtk_tree_view_get_level_indentation (GtkTreeView *tree_view); - -/* Convenience functions for setting tooltips */ -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_set_tooltip_row (GtkTreeView *tree_view, - GtkTooltip *tooltip, - GtkTreePath *path); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_set_tooltip_cell (GtkTreeView *tree_view, - GtkTooltip *tooltip, - GtkTreePath *path, - GtkTreeViewColumn *column, - GtkCellRenderer *cell); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_view_get_tooltip_context(GtkTreeView *tree_view, - int x, - int y, - gboolean keyboard_tip, - GtkTreeModel **model, - GtkTreePath **path, - GtkTreeIter *iter); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_set_tooltip_column (GtkTreeView *tree_view, - int column); -GDK_AVAILABLE_IN_ALL -int gtk_tree_view_get_tooltip_column (GtkTreeView *tree_view); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkTreeView, g_object_unref) - -G_END_DECLS - - -#endif /* __GTK_TREE_VIEW_H__ */ diff --git a/gtk/gtktreeviewcolumn.c b/gtk/gtktreeviewcolumn.c deleted file mode 100644 index 688f70beeb..0000000000 --- a/gtk/gtktreeviewcolumn.c +++ /dev/null @@ -1,3052 +0,0 @@ -/* gtktreeviewcolumn.c - * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#include "config.h" - -#include "gtktreeviewcolumn.h" - -#include "gtkbox.h" -#include "gtkbutton.h" -#include "gtkcellareabox.h" -#include "gtkcellareacontext.h" -#include "gtkcelllayout.h" -#include "gtkdragsourceprivate.h" -#include "gtkframe.h" -#include "gtkimage.h" -#include "gtklabel.h" -#include "gtkmarshalers.h" -#include "gtkprivate.h" -#include "gtktreeprivate.h" -#include "gtktreeview.h" -#include "gtktypebuiltins.h" -#include "gtkwidgetprivate.h" -#include "gtkgesturedrag.h" -#include "gtkeventcontrollerfocus.h" -#include "gtkeventcontrollerkey.h" -#include "gtkbuiltiniconprivate.h" - -#include - - -/** - * GtkTreeViewColumn: - * - * A visible column in a [class@Gtk.TreeView] widget - * - * The `GtkTreeViewColumn` object represents a visible column in a `GtkTreeView` widget. - * It allows to set properties of the column header, and functions as a holding pen - * for the cell renderers which determine how the data in the column is displayed. - * - * Please refer to the [tree widget conceptual overview](section-tree-widget.html) - * for an overview of all the objects and data types related to the tree widget and - * how they work together, and to the [class@Gtk.TreeView] documentation for specifics - * about the CSS node structure for treeviews and their headers. - */ - - -/* Type methods */ -static void gtk_tree_view_column_cell_layout_init (GtkCellLayoutIface *iface); - -/* GObject methods */ -static void gtk_tree_view_column_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec); -static void gtk_tree_view_column_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec); -static void gtk_tree_view_column_finalize (GObject *object); -static void gtk_tree_view_column_dispose (GObject *object); -static void gtk_tree_view_column_constructed (GObject *object); - -/* GtkCellLayout implementation */ -static void gtk_tree_view_column_ensure_cell_area (GtkTreeViewColumn *column, - GtkCellArea *cell_area); - -static GtkCellArea *gtk_tree_view_column_cell_layout_get_area (GtkCellLayout *cell_layout); - -/* Button handling code */ -static void gtk_tree_view_column_create_button (GtkTreeViewColumn *tree_column); -static void gtk_tree_view_column_update_button (GtkTreeViewColumn *tree_column); - -/* Button signal handlers */ -static void column_button_drag_begin (GtkGestureDrag *gesture, - double x, - double y, - GtkTreeViewColumn *column); -static void column_button_drag_update (GtkGestureDrag *gesture, - double offset_x, - double offset_y, - GtkTreeViewColumn *column); - -static void gtk_tree_view_column_button_clicked (GtkWidget *widget, - gpointer data); -static gboolean gtk_tree_view_column_mnemonic_activate (GtkWidget *widget, - gboolean group_cycling, - gpointer data); - -/* Property handlers */ -static void gtk_tree_view_model_sort_column_changed (GtkTreeSortable *sortable, - GtkTreeViewColumn *tree_column); - -/* GtkCellArea/GtkCellAreaContext callbacks */ -static void gtk_tree_view_column_context_changed (GtkCellAreaContext *context, - GParamSpec *pspec, - GtkTreeViewColumn *tree_column); -static void gtk_tree_view_column_add_editable_callback (GtkCellArea *area, - GtkCellRenderer *renderer, - GtkCellEditable *edit_widget, - GdkRectangle *cell_area, - const char *path_string, - gpointer user_data); -static void gtk_tree_view_column_remove_editable_callback (GtkCellArea *area, - GtkCellRenderer *renderer, - GtkCellEditable *edit_widget, - gpointer user_data); - -/* Internal functions */ -static void gtk_tree_view_column_sort (GtkTreeViewColumn *tree_column, - gpointer data); -static void gtk_tree_view_column_setup_sort_column_id_callback (GtkTreeViewColumn *tree_column); -static void gtk_tree_view_column_set_attributesv (GtkTreeViewColumn *tree_column, - GtkCellRenderer *cell_renderer, - va_list args); - -/* GtkBuildable implementation */ -static void gtk_tree_view_column_buildable_init (GtkBuildableIface *iface); - -typedef struct _GtkTreeViewColumnClass GtkTreeViewColumnClass; -typedef struct _GtkTreeViewColumnPrivate GtkTreeViewColumnPrivate; - -struct _GtkTreeViewColumn -{ - GInitiallyUnowned parent_instance; - - GtkTreeViewColumnPrivate *priv; -}; - -struct _GtkTreeViewColumnClass -{ - GInitiallyUnownedClass parent_class; - - void (*clicked) (GtkTreeViewColumn *tree_column); -}; - - -struct _GtkTreeViewColumnPrivate -{ - GtkWidget *tree_view; - GtkWidget *button; - GtkWidget *child; - GtkWidget *arrow; - GtkWidget *frame; - gulong property_changed_signal; - float xalign; - - /* Sizing fields */ - /* see gtk+/doc/tree-column-sizing.txt for more information on them */ - GtkTreeViewColumnSizing column_type; - int padding; - int x_offset; - int width; - int fixed_width; - int min_width; - int max_width; - - /* dragging columns */ - int drag_x; - int drag_y; - - char *title; - - /* Sorting */ - gulong sort_clicked_signal; - gulong sort_column_changed_signal; - int sort_column_id; - GtkSortType sort_order; - - /* Cell area */ - GtkCellArea *cell_area; - GtkCellAreaContext *cell_area_context; - gulong add_editable_signal; - gulong remove_editable_signal; - gulong context_changed_signal; - - /* Flags */ - guint visible : 1; - guint resizable : 1; - guint clickable : 1; - guint dirty : 1; - guint show_sort_indicator : 1; - guint maybe_reordered : 1; - guint reorderable : 1; - guint expand : 1; -}; - -enum -{ - PROP_0, - PROP_VISIBLE, - PROP_RESIZABLE, - PROP_X_OFFSET, - PROP_WIDTH, - PROP_SPACING, - PROP_SIZING, - PROP_FIXED_WIDTH, - PROP_MIN_WIDTH, - PROP_MAX_WIDTH, - PROP_TITLE, - PROP_EXPAND, - PROP_CLICKABLE, - PROP_WIDGET, - PROP_ALIGNMENT, - PROP_REORDERABLE, - PROP_SORT_INDICATOR, - PROP_SORT_ORDER, - PROP_SORT_COLUMN_ID, - PROP_CELL_AREA, - LAST_PROP -}; - -enum -{ - CLICKED, - LAST_SIGNAL -}; - -static guint tree_column_signals[LAST_SIGNAL] = { 0 }; -static GParamSpec *tree_column_props[LAST_PROP] = { NULL, }; - -G_DEFINE_TYPE_WITH_CODE (GtkTreeViewColumn, gtk_tree_view_column, G_TYPE_INITIALLY_UNOWNED, - G_ADD_PRIVATE (GtkTreeViewColumn) - G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT, - gtk_tree_view_column_cell_layout_init) - G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, - gtk_tree_view_column_buildable_init)) - - -static void -gtk_tree_view_column_class_init (GtkTreeViewColumnClass *class) -{ - GObjectClass *object_class; - - object_class = (GObjectClass*) class; - - class->clicked = NULL; - - object_class->constructed = gtk_tree_view_column_constructed; - object_class->finalize = gtk_tree_view_column_finalize; - object_class->dispose = gtk_tree_view_column_dispose; - object_class->set_property = gtk_tree_view_column_set_property; - object_class->get_property = gtk_tree_view_column_get_property; - - /** - * GtkTreeViewColumn::clicked: - * @column: the `GtkTreeViewColumn` that emitted the signal - * - * Emitted when the column's header has been clicked. - */ - tree_column_signals[CLICKED] = - g_signal_new (I_("clicked"), - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkTreeViewColumnClass, clicked), - NULL, NULL, - NULL, - G_TYPE_NONE, 0); - - tree_column_props[PROP_VISIBLE] = - g_param_spec_boolean ("visible", NULL, NULL, - TRUE, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); - - tree_column_props[PROP_RESIZABLE] = - g_param_spec_boolean ("resizable", NULL, NULL, - FALSE, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); - - tree_column_props[PROP_X_OFFSET] = - g_param_spec_int ("x-offset", NULL, NULL, - -G_MAXINT, G_MAXINT, - 0, - GTK_PARAM_READABLE); - - tree_column_props[PROP_WIDTH] = - g_param_spec_int ("width", NULL, NULL, - 0, G_MAXINT, - 0, - GTK_PARAM_READABLE); - - tree_column_props[PROP_SPACING] = - g_param_spec_int ("spacing", NULL, NULL, - 0, G_MAXINT, - 0, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); - - tree_column_props[PROP_SIZING] = - g_param_spec_enum ("sizing", NULL, NULL, - GTK_TYPE_TREE_VIEW_COLUMN_SIZING, - GTK_TREE_VIEW_COLUMN_GROW_ONLY, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); - - tree_column_props[PROP_FIXED_WIDTH] = - g_param_spec_int ("fixed-width", NULL, NULL, - -1, G_MAXINT, - -1, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); - - tree_column_props[PROP_MIN_WIDTH] = - g_param_spec_int ("min-width", NULL, NULL, - -1, G_MAXINT, - -1, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); - - tree_column_props[PROP_MAX_WIDTH] = - g_param_spec_int ("max-width", NULL, NULL, - -1, G_MAXINT, - -1, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); - - tree_column_props[PROP_TITLE] = - g_param_spec_string ("title", NULL, NULL, - "", - GTK_PARAM_READWRITE); - - tree_column_props[PROP_EXPAND] = - g_param_spec_boolean ("expand", NULL, NULL, - FALSE, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); - - tree_column_props[PROP_CLICKABLE] = - g_param_spec_boolean ("clickable", NULL, NULL, - FALSE, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); - - tree_column_props[PROP_WIDGET] = - g_param_spec_object ("widget", NULL, NULL, - GTK_TYPE_WIDGET, - GTK_PARAM_READWRITE); - - tree_column_props[PROP_ALIGNMENT] = - g_param_spec_float ("alignment", NULL, NULL, - 0.0, 1.0, 0.0, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); - - tree_column_props[PROP_REORDERABLE] = - g_param_spec_boolean ("reorderable", NULL, NULL, - FALSE, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); - - tree_column_props[PROP_SORT_INDICATOR] = - g_param_spec_boolean ("sort-indicator", NULL, NULL, - FALSE, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); - - tree_column_props[PROP_SORT_ORDER] = - g_param_spec_enum ("sort-order", NULL, NULL, - GTK_TYPE_SORT_TYPE, - GTK_SORT_ASCENDING, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); - - /** - * GtkTreeViewColumn:sort-column-id: - * - * Logical sort column ID this column sorts on when selected for sorting. Setting the sort column ID makes the column header - * clickable. Set to -1 to make the column unsortable. - **/ - tree_column_props[PROP_SORT_COLUMN_ID] = - g_param_spec_int ("sort-column-id", NULL, NULL, - -1, G_MAXINT, - -1, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); - - /** - * GtkTreeViewColumn:cell-area: - * - * The `GtkCellArea` used to layout cell renderers for this column. - * - * If no area is specified when creating the tree view column with gtk_tree_view_column_new_with_area() - * a horizontally oriented `GtkCellAreaBox` will be used. - */ - tree_column_props[PROP_CELL_AREA] = - g_param_spec_object ("cell-area", NULL, NULL, - GTK_TYPE_CELL_AREA, - GTK_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY); - - g_object_class_install_properties (object_class, LAST_PROP, tree_column_props); -} - -static void -gtk_tree_view_column_custom_tag_end (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const char *tagname, - gpointer data) -{ - /* Just ignore the boolean return from here */ - _gtk_cell_layout_buildable_custom_tag_end (buildable, builder, child, tagname, data); -} - -static void -gtk_tree_view_column_buildable_init (GtkBuildableIface *iface) -{ - iface->add_child = _gtk_cell_layout_buildable_add_child; - iface->custom_tag_start = _gtk_cell_layout_buildable_custom_tag_start; - iface->custom_tag_end = gtk_tree_view_column_custom_tag_end; -} - -static void -gtk_tree_view_column_cell_layout_init (GtkCellLayoutIface *iface) -{ - iface->get_area = gtk_tree_view_column_cell_layout_get_area; -} - -static void -gtk_tree_view_column_init (GtkTreeViewColumn *tree_column) -{ - GtkTreeViewColumnPrivate *priv; - - tree_column->priv = gtk_tree_view_column_get_instance_private (tree_column); - priv = tree_column->priv; - - priv->button = NULL; - priv->xalign = 0.0; - priv->width = 0; - priv->padding = 0; - priv->min_width = -1; - priv->max_width = -1; - priv->column_type = GTK_TREE_VIEW_COLUMN_GROW_ONLY; - priv->visible = TRUE; - priv->resizable = FALSE; - priv->expand = FALSE; - priv->clickable = FALSE; - priv->dirty = TRUE; - priv->sort_order = GTK_SORT_ASCENDING; - priv->show_sort_indicator = FALSE; - priv->property_changed_signal = 0; - priv->sort_clicked_signal = 0; - priv->sort_column_changed_signal = 0; - priv->sort_column_id = -1; - priv->reorderable = FALSE; - priv->maybe_reordered = FALSE; - priv->fixed_width = -1; - priv->title = g_strdup (""); - - gtk_tree_view_column_create_button (tree_column); -} - -static void -gtk_tree_view_column_constructed (GObject *object) -{ - GtkTreeViewColumn *tree_column = GTK_TREE_VIEW_COLUMN (object); - - G_OBJECT_CLASS (gtk_tree_view_column_parent_class)->constructed (object); - - gtk_tree_view_column_ensure_cell_area (tree_column, NULL); -} - -static void -gtk_tree_view_column_dispose (GObject *object) -{ - GtkTreeViewColumn *tree_column = (GtkTreeViewColumn *) object; - GtkTreeViewColumnPrivate *priv = tree_column->priv; - - /* Remove this column from its treeview, - * in case this column is destroyed before its treeview. - */ - if (priv->tree_view) - gtk_tree_view_remove_column (GTK_TREE_VIEW (priv->tree_view), tree_column); - - if (priv->cell_area_context) - { - g_signal_handler_disconnect (priv->cell_area_context, - priv->context_changed_signal); - - g_object_unref (priv->cell_area_context); - - priv->cell_area_context = NULL; - priv->context_changed_signal = 0; - } - - if (priv->cell_area) - { - g_signal_handler_disconnect (priv->cell_area, - priv->add_editable_signal); - g_signal_handler_disconnect (priv->cell_area, - priv->remove_editable_signal); - - g_object_unref (priv->cell_area); - priv->cell_area = NULL; - priv->add_editable_signal = 0; - priv->remove_editable_signal = 0; - } - - if (priv->child) - { - g_object_unref (priv->child); - priv->child = NULL; - } - - g_clear_object (&priv->button); - - G_OBJECT_CLASS (gtk_tree_view_column_parent_class)->dispose (object); -} - -static void -gtk_tree_view_column_finalize (GObject *object) -{ - GtkTreeViewColumn *tree_column = (GtkTreeViewColumn *) object; - GtkTreeViewColumnPrivate *priv = tree_column->priv; - - g_free (priv->title); - - G_OBJECT_CLASS (gtk_tree_view_column_parent_class)->finalize (object); -} - -static void -gtk_tree_view_column_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - GtkTreeViewColumn *tree_column; - GtkCellArea *area; - - tree_column = GTK_TREE_VIEW_COLUMN (object); - - switch (prop_id) - { - case PROP_VISIBLE: - gtk_tree_view_column_set_visible (tree_column, - g_value_get_boolean (value)); - break; - - case PROP_RESIZABLE: - gtk_tree_view_column_set_resizable (tree_column, - g_value_get_boolean (value)); - break; - - case PROP_SIZING: - gtk_tree_view_column_set_sizing (tree_column, - g_value_get_enum (value)); - break; - - case PROP_FIXED_WIDTH: - gtk_tree_view_column_set_fixed_width (tree_column, - g_value_get_int (value)); - break; - - case PROP_MIN_WIDTH: - gtk_tree_view_column_set_min_width (tree_column, - g_value_get_int (value)); - break; - - case PROP_MAX_WIDTH: - gtk_tree_view_column_set_max_width (tree_column, - g_value_get_int (value)); - break; - - case PROP_SPACING: - gtk_tree_view_column_set_spacing (tree_column, - g_value_get_int (value)); - break; - - case PROP_TITLE: - gtk_tree_view_column_set_title (tree_column, - g_value_get_string (value)); - break; - - case PROP_EXPAND: - gtk_tree_view_column_set_expand (tree_column, - g_value_get_boolean (value)); - break; - - case PROP_CLICKABLE: - gtk_tree_view_column_set_clickable (tree_column, - g_value_get_boolean (value)); - break; - - case PROP_WIDGET: - gtk_tree_view_column_set_widget (tree_column, - (GtkWidget*) g_value_get_object (value)); - break; - - case PROP_ALIGNMENT: - gtk_tree_view_column_set_alignment (tree_column, - g_value_get_float (value)); - break; - - case PROP_REORDERABLE: - gtk_tree_view_column_set_reorderable (tree_column, - g_value_get_boolean (value)); - break; - - case PROP_SORT_INDICATOR: - gtk_tree_view_column_set_sort_indicator (tree_column, - g_value_get_boolean (value)); - break; - - case PROP_SORT_ORDER: - gtk_tree_view_column_set_sort_order (tree_column, - g_value_get_enum (value)); - break; - - case PROP_SORT_COLUMN_ID: - gtk_tree_view_column_set_sort_column_id (tree_column, - g_value_get_int (value)); - break; - - case PROP_CELL_AREA: - /* Construct-only, can only be assigned once */ - area = g_value_get_object (value); - - if (area) - { - if (tree_column->priv->cell_area != NULL) - { - g_warning ("cell-area has already been set, ignoring construct property"); - g_object_ref_sink (area); - g_object_unref (area); - } - else - gtk_tree_view_column_ensure_cell_area (tree_column, area); - } - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gtk_tree_view_column_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GtkTreeViewColumn *tree_column; - - tree_column = GTK_TREE_VIEW_COLUMN (object); - - switch (prop_id) - { - case PROP_VISIBLE: - g_value_set_boolean (value, - gtk_tree_view_column_get_visible (tree_column)); - break; - - case PROP_RESIZABLE: - g_value_set_boolean (value, - gtk_tree_view_column_get_resizable (tree_column)); - break; - - case PROP_X_OFFSET: - g_value_set_int (value, - gtk_tree_view_column_get_x_offset (tree_column)); - break; - - case PROP_WIDTH: - g_value_set_int (value, - gtk_tree_view_column_get_width (tree_column)); - break; - - case PROP_SPACING: - g_value_set_int (value, - gtk_tree_view_column_get_spacing (tree_column)); - break; - - case PROP_SIZING: - g_value_set_enum (value, - gtk_tree_view_column_get_sizing (tree_column)); - break; - - case PROP_FIXED_WIDTH: - g_value_set_int (value, - gtk_tree_view_column_get_fixed_width (tree_column)); - break; - - case PROP_MIN_WIDTH: - g_value_set_int (value, - gtk_tree_view_column_get_min_width (tree_column)); - break; - - case PROP_MAX_WIDTH: - g_value_set_int (value, - gtk_tree_view_column_get_max_width (tree_column)); - break; - - case PROP_TITLE: - g_value_set_string (value, - gtk_tree_view_column_get_title (tree_column)); - break; - - case PROP_EXPAND: - g_value_set_boolean (value, - gtk_tree_view_column_get_expand (tree_column)); - break; - - case PROP_CLICKABLE: - g_value_set_boolean (value, - gtk_tree_view_column_get_clickable (tree_column)); - break; - - case PROP_WIDGET: - g_value_set_object (value, - (GObject*) gtk_tree_view_column_get_widget (tree_column)); - break; - - case PROP_ALIGNMENT: - g_value_set_float (value, - gtk_tree_view_column_get_alignment (tree_column)); - break; - - case PROP_REORDERABLE: - g_value_set_boolean (value, - gtk_tree_view_column_get_reorderable (tree_column)); - break; - - case PROP_SORT_INDICATOR: - g_value_set_boolean (value, - gtk_tree_view_column_get_sort_indicator (tree_column)); - break; - - case PROP_SORT_ORDER: - g_value_set_enum (value, - gtk_tree_view_column_get_sort_order (tree_column)); - break; - - case PROP_SORT_COLUMN_ID: - g_value_set_int (value, - gtk_tree_view_column_get_sort_column_id (tree_column)); - break; - - case PROP_CELL_AREA: - g_value_set_object (value, tree_column->priv->cell_area); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -/* Implementation of GtkCellLayout interface - */ - -static void -gtk_tree_view_column_ensure_cell_area (GtkTreeViewColumn *column, - GtkCellArea *cell_area) -{ - GtkTreeViewColumnPrivate *priv = column->priv; - - if (priv->cell_area) - return; - - if (cell_area) - priv->cell_area = cell_area; - else - priv->cell_area = gtk_cell_area_box_new (); - - g_object_ref_sink (priv->cell_area); - - priv->add_editable_signal = - g_signal_connect (priv->cell_area, "add-editable", - G_CALLBACK (gtk_tree_view_column_add_editable_callback), - column); - priv->remove_editable_signal = - g_signal_connect (priv->cell_area, "remove-editable", - G_CALLBACK (gtk_tree_view_column_remove_editable_callback), - column); - - priv->cell_area_context = gtk_cell_area_create_context (priv->cell_area); - - priv->context_changed_signal = - g_signal_connect (priv->cell_area_context, "notify", - G_CALLBACK (gtk_tree_view_column_context_changed), - column); -} - -static GtkCellArea * -gtk_tree_view_column_cell_layout_get_area (GtkCellLayout *cell_layout) -{ - GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN (cell_layout); - GtkTreeViewColumnPrivate *priv = column->priv; - - if (G_UNLIKELY (!priv->cell_area)) - gtk_tree_view_column_ensure_cell_area (column, NULL); - - return priv->cell_area; -} - -static void -focus_in (GtkEventControllerKey *controller, - GtkTreeViewColumn *column) -{ - _gtk_tree_view_set_focus_column (GTK_TREE_VIEW (column->priv->tree_view), column); -} - -/* Button handling code - */ -static void -gtk_tree_view_column_create_button (GtkTreeViewColumn *tree_column) -{ - GtkTreeViewColumnPrivate *priv = tree_column->priv; - GtkEventController *controller; - GtkWidget *child; - GtkWidget *hbox; - - g_return_if_fail (priv->button == NULL); - - priv->button = gtk_button_new (); - g_object_ref_sink (priv->button); - gtk_widget_set_focus_on_click (priv->button, FALSE); - gtk_widget_set_overflow (priv->button, GTK_OVERFLOW_HIDDEN); - - g_signal_connect (priv->button, "clicked", - G_CALLBACK (gtk_tree_view_column_button_clicked), - tree_column); - - controller = GTK_EVENT_CONTROLLER (gtk_gesture_drag_new ()); - g_signal_connect (controller, "drag-begin", - G_CALLBACK (column_button_drag_begin), tree_column); - g_signal_connect (controller, "drag-update", - G_CALLBACK (column_button_drag_update), tree_column); - gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_CAPTURE); - gtk_widget_add_controller (priv->button, controller); - - controller = gtk_event_controller_focus_new (); - g_signal_connect (controller, "enter", G_CALLBACK (focus_in), tree_column); - gtk_widget_add_controller (priv->button, controller); - - priv->frame = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); - gtk_widget_set_hexpand (priv->frame, TRUE); - gtk_widget_set_halign (priv->frame, GTK_ALIGN_START); - - hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2); - priv->arrow = gtk_builtin_icon_new ("sort-indicator"); - - if (priv->child) - child = priv->child; - else - child = gtk_label_new (priv->title); - - g_signal_connect (child, "mnemonic-activate", - G_CALLBACK (gtk_tree_view_column_mnemonic_activate), - tree_column); - - if (priv->xalign <= 0.5) - { - gtk_box_append (GTK_BOX (hbox), priv->frame); - gtk_box_append (GTK_BOX (hbox), priv->arrow); - } - else - { - gtk_box_append (GTK_BOX (hbox), priv->arrow); - gtk_box_append (GTK_BOX (hbox), priv->frame); - } - - gtk_box_append (GTK_BOX (priv->frame), child); - gtk_button_set_child (GTK_BUTTON (priv->button), hbox); -} - -static void -gtk_tree_view_column_update_button (GtkTreeViewColumn *tree_column) -{ - GtkTreeViewColumnPrivate *priv = tree_column->priv; - int sort_column_id = -1; - GtkWidget *hbox; - GtkWidget *frame; - GtkWidget *arrow; - GtkWidget *current_child; - GtkTreeModel *model; - - if (priv->tree_view) - model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree_view)); - else - model = NULL; - - hbox = gtk_button_get_child (GTK_BUTTON (priv->button)); - frame = priv->frame; - arrow = priv->arrow; - current_child = gtk_widget_get_first_child (frame); - - /* Set up the actual button */ - if (priv->child) - { - if (current_child != priv->child) - { - gtk_box_remove (GTK_BOX (frame), current_child); - gtk_box_append (GTK_BOX (frame), priv->child); - } - } - else - { - if (current_child == NULL) - { - current_child = gtk_label_new (NULL); - gtk_widget_show (current_child); - gtk_box_append (GTK_BOX (frame), current_child); - } - - g_return_if_fail (GTK_IS_LABEL (current_child)); - - if (priv->title) - gtk_label_set_text_with_mnemonic (GTK_LABEL (current_child), - priv->title); - else - gtk_label_set_text_with_mnemonic (GTK_LABEL (current_child), - ""); - } - - if (GTK_IS_TREE_SORTABLE (model)) - gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (model), - &sort_column_id, - NULL); - - if (priv->show_sort_indicator) - { - gboolean alternative; - - if (priv->tree_view) - g_object_get (gtk_widget_get_settings (priv->tree_view), - "gtk-alternative-sort-arrows", &alternative, - NULL); - else - alternative = FALSE; - - if ((!alternative && priv->sort_order == GTK_SORT_ASCENDING) || - (alternative && priv->sort_order == GTK_SORT_DESCENDING)) - { - gtk_widget_remove_css_class (arrow, "descending"); - gtk_widget_add_css_class (arrow, "ascending"); - } - else - { - gtk_widget_remove_css_class (arrow, "ascending"); - gtk_widget_add_css_class (arrow, "descending"); - } - } - - /* Put arrow on the right if the text is left-or-center justified, and on the - * left otherwise; do this by packing boxes, so flipping text direction will - * reverse things - */ - if (priv->xalign <= 0.5) - gtk_box_reorder_child_after (GTK_BOX (hbox), arrow, gtk_widget_get_last_child (hbox)); - else - gtk_box_reorder_child_after (GTK_BOX (hbox), arrow, NULL); - - if (priv->show_sort_indicator - || (GTK_IS_TREE_SORTABLE (model) && priv->sort_column_id >= 0)) - gtk_widget_show (arrow); - else - gtk_widget_hide (arrow); - - if (priv->show_sort_indicator) - gtk_widget_set_opacity (arrow, 1.0); - else - gtk_widget_set_opacity (arrow, 0.0); - - /* It's always safe to hide the button. It isn't always safe to show it, as - * if you show it before it's realized, it'll get the wrong window. */ - if (priv->tree_view != NULL && - gtk_widget_get_realized (priv->tree_view)) - { - if (priv->visible && - gtk_tree_view_get_headers_visible (GTK_TREE_VIEW (priv->tree_view))) - { - gtk_widget_show (priv->button); - } - else - { - gtk_widget_hide (priv->button); - } - } - - if (priv->reorderable || priv->clickable) - { - gtk_widget_set_focusable (priv->button, TRUE); - } - else - { - gtk_widget_set_focusable (priv->button, FALSE); - if (gtk_widget_has_focus (priv->button)) - { - GtkRoot *root = gtk_widget_get_root (priv->tree_view); - gtk_root_set_focus (root, NULL); - } - } - /* Queue a resize on the assumption that we always want to catch all changes - * and columns don't change all that often. - */ - if (priv->tree_view && gtk_widget_get_realized (priv->tree_view)) - gtk_widget_queue_resize (priv->tree_view); -} - -/* Button signal handlers - */ - -static void -column_button_drag_begin (GtkGestureDrag *gesture, - double x, - double y, - GtkTreeViewColumn *column) -{ - GtkTreeViewColumnPrivate *priv = column->priv; - - if (!priv->reorderable) - { - gtk_gesture_set_state (GTK_GESTURE (gesture), - GTK_EVENT_SEQUENCE_DENIED); - return; - } - - priv->drag_x = x; - priv->drag_y = y; - gtk_widget_grab_focus (priv->button); -} - -static void -column_button_drag_update (GtkGestureDrag *gesture, - double offset_x, - double offset_y, - GtkTreeViewColumn *column) -{ - GtkTreeViewColumnPrivate *priv = column->priv; - - if (gtk_drag_check_threshold_double (priv->button, 0, 0, offset_x, offset_y)) - { - gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED); - _gtk_tree_view_column_start_drag (GTK_TREE_VIEW (priv->tree_view), column, - gtk_gesture_get_device (GTK_GESTURE (gesture))); - } -} - -static void -gtk_tree_view_column_button_clicked (GtkWidget *widget, gpointer data) -{ - g_signal_emit_by_name (data, "clicked"); -} - -static gboolean -gtk_tree_view_column_mnemonic_activate (GtkWidget *widget, - gboolean group_cycling, - gpointer data) -{ - GtkTreeViewColumn *column = (GtkTreeViewColumn *)data; - GtkTreeViewColumnPrivate *priv = column->priv; - - g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (column), FALSE); - - _gtk_tree_view_set_focus_column (GTK_TREE_VIEW (priv->tree_view), column); - - if (priv->clickable) - g_signal_emit_by_name (priv->button, "clicked"); - else if (gtk_widget_get_focusable (priv->button)) - gtk_widget_grab_focus (priv->button); - else - gtk_widget_grab_focus (priv->tree_view); - - return TRUE; -} - -static void -gtk_tree_view_model_sort_column_changed (GtkTreeSortable *sortable, - GtkTreeViewColumn *column) -{ - GtkTreeViewColumnPrivate *priv = column->priv; - int sort_column_id; - GtkSortType order; - - if (gtk_tree_sortable_get_sort_column_id (sortable, - &sort_column_id, - &order)) - { - if (sort_column_id == priv->sort_column_id) - { - gtk_tree_view_column_set_sort_indicator (column, TRUE); - gtk_tree_view_column_set_sort_order (column, order); - } - else - { - gtk_tree_view_column_set_sort_indicator (column, FALSE); - } - } - else - { - gtk_tree_view_column_set_sort_indicator (column, FALSE); - } -} - -static void -gtk_tree_view_column_sort (GtkTreeViewColumn *tree_column, - gpointer data) -{ - GtkTreeViewColumnPrivate *priv = tree_column->priv; - GtkTreeModel *model; - GtkTreeSortable *sortable; - int sort_column_id; - GtkSortType order; - gboolean has_sort_column; - gboolean has_default_sort_func; - - g_return_if_fail (priv->tree_view != NULL); - - model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree_view)); - sortable = GTK_TREE_SORTABLE (model); - - has_sort_column = - gtk_tree_sortable_get_sort_column_id (sortable, - &sort_column_id, - &order); - has_default_sort_func = - gtk_tree_sortable_has_default_sort_func (sortable); - - if (has_sort_column && - sort_column_id == priv->sort_column_id) - { - if (order == GTK_SORT_ASCENDING) - gtk_tree_sortable_set_sort_column_id (sortable, - priv->sort_column_id, - GTK_SORT_DESCENDING); - else if (order == GTK_SORT_DESCENDING && has_default_sort_func) - gtk_tree_sortable_set_sort_column_id (sortable, - GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, - GTK_SORT_ASCENDING); - else - gtk_tree_sortable_set_sort_column_id (sortable, - priv->sort_column_id, - GTK_SORT_ASCENDING); - } - else - { - gtk_tree_sortable_set_sort_column_id (sortable, - priv->sort_column_id, - GTK_SORT_ASCENDING); - } -} - -static void -gtk_tree_view_column_setup_sort_column_id_callback (GtkTreeViewColumn *tree_column) -{ - GtkTreeViewColumnPrivate *priv = tree_column->priv; - GtkTreeModel *model; - - if (priv->tree_view == NULL) - return; - - model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree_view)); - - if (model == NULL) - return; - - if (GTK_IS_TREE_SORTABLE (model) && - priv->sort_column_id != -1) - { - int real_sort_column_id; - GtkSortType real_order; - - if (priv->sort_column_changed_signal == 0) - priv->sort_column_changed_signal = - g_signal_connect (model, "sort-column-changed", - G_CALLBACK (gtk_tree_view_model_sort_column_changed), - tree_column); - - if (gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (model), - &real_sort_column_id, - &real_order) && - (real_sort_column_id == priv->sort_column_id)) - { - gtk_tree_view_column_set_sort_indicator (tree_column, TRUE); - gtk_tree_view_column_set_sort_order (tree_column, real_order); - } - else - { - gtk_tree_view_column_set_sort_indicator (tree_column, FALSE); - } - } -} - -static void -gtk_tree_view_column_context_changed (GtkCellAreaContext *context, - GParamSpec *pspec, - GtkTreeViewColumn *tree_column) -{ - /* Here we want the column re-requested if the underlying context was - * actually reset for any reason, this can happen if the underlying - * area/cell configuration changes (i.e. cell packing properties - * or cell spacing and the like) - * - * Note that we block this handler while requesting for sizes - * so there is no need to check for the new context size being -1, - * we also block the handler when explicitly resetting the context - * so as to avoid some infinite stack recursion. - */ - if (!strcmp (pspec->name, "minimum-width") || - !strcmp (pspec->name, "natural-width") || - !strcmp (pspec->name, "minimum-height") || - !strcmp (pspec->name, "natural-height")) - _gtk_tree_view_column_cell_set_dirty (tree_column, TRUE); -} - -static void -gtk_tree_view_column_add_editable_callback (GtkCellArea *area, - GtkCellRenderer *renderer, - GtkCellEditable *edit_widget, - GdkRectangle *cell_area, - const char *path_string, - gpointer user_data) -{ - GtkTreeViewColumn *column = user_data; - GtkTreeViewColumnPrivate *priv = column->priv; - GtkTreePath *path; - - if (priv->tree_view) - { - path = gtk_tree_path_new_from_string (path_string); - - _gtk_tree_view_add_editable (GTK_TREE_VIEW (priv->tree_view), - column, - path, - edit_widget, - cell_area); - - gtk_tree_path_free (path); - } -} - -static void -gtk_tree_view_column_remove_editable_callback (GtkCellArea *area, - GtkCellRenderer *renderer, - GtkCellEditable *edit_widget, - gpointer user_data) -{ - GtkTreeViewColumn *column = user_data; - GtkTreeViewColumnPrivate *priv = column->priv; - - if (priv->tree_view) - _gtk_tree_view_remove_editable (GTK_TREE_VIEW (priv->tree_view), - column, - edit_widget); -} - -/* Exported Private Functions. - * These should only be called by gtktreeview.c or gtktreeviewcolumn.c - */ -void -_gtk_tree_view_column_realize_button (GtkTreeViewColumn *column) -{ - g_return_if_fail (GTK_IS_TREE_VIEW (column->priv->tree_view)); - g_return_if_fail (gtk_widget_get_realized (column->priv->tree_view)); - g_return_if_fail (column->priv->button != NULL); - - gtk_tree_view_column_update_button (column); -} - -void -_gtk_tree_view_column_unset_model (GtkTreeViewColumn *column, - GtkTreeModel *old_model) -{ - GtkTreeViewColumnPrivate *priv = column->priv; - - if (priv->sort_column_changed_signal) - { - g_signal_handler_disconnect (old_model, - priv->sort_column_changed_signal); - priv->sort_column_changed_signal = 0; - } - gtk_tree_view_column_set_sort_indicator (column, FALSE); -} - -void -_gtk_tree_view_column_set_tree_view (GtkTreeViewColumn *column, - GtkTreeView *tree_view) -{ - GtkTreeViewColumnPrivate *priv = column->priv; - - g_assert (priv->tree_view == NULL); - - priv->tree_view = GTK_WIDGET (tree_view); - - /* avoid a warning with our messed up CSS nodes */ - gtk_widget_insert_after (priv->button, GTK_WIDGET (tree_view), NULL); - - priv->property_changed_signal = - g_signal_connect_swapped (tree_view, - "notify::model", - G_CALLBACK (gtk_tree_view_column_setup_sort_column_id_callback), - column); - - gtk_tree_view_column_setup_sort_column_id_callback (column); -} - -void -_gtk_tree_view_column_unset_tree_view (GtkTreeViewColumn *column) -{ - GtkTreeViewColumnPrivate *priv = column->priv; - - if (priv->tree_view == NULL) - return; - - gtk_widget_unparent (priv->button); - - if (priv->property_changed_signal) - { - g_signal_handler_disconnect (priv->tree_view, priv->property_changed_signal); - priv->property_changed_signal = 0; - } - - if (priv->sort_column_changed_signal) - { - g_signal_handler_disconnect (gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree_view)), - priv->sort_column_changed_signal); - priv->sort_column_changed_signal = 0; - } - - priv->tree_view = NULL; -} - -gboolean -_gtk_tree_view_column_has_editable_cell (GtkTreeViewColumn *column) -{ - GtkTreeViewColumnPrivate *priv = column->priv; - gboolean ret = FALSE; - GList *list, *cells; - - cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (priv->cell_area)); - - for (list = cells; list; list = list->next) - { - GtkCellRenderer *cell = list->data; - GtkCellRendererMode mode; - - g_object_get (cell, "mode", &mode, NULL); - - if (mode == GTK_CELL_RENDERER_MODE_EDITABLE) - { - ret = TRUE; - break; - } - } - - g_list_free (cells); - - return ret; -} - -/* gets cell being edited */ -GtkCellRenderer * -_gtk_tree_view_column_get_edited_cell (GtkTreeViewColumn *column) -{ - GtkTreeViewColumnPrivate *priv = column->priv; - - return gtk_cell_area_get_edited_cell (priv->cell_area); -} - -GtkCellRenderer * -_gtk_tree_view_column_get_cell_at_pos (GtkTreeViewColumn *column, - GdkRectangle *cell_area, - GdkRectangle *background_area, - int x, - int y) -{ - GtkCellRenderer *match = NULL; - GtkTreeViewColumnPrivate *priv = column->priv; - - /* If (x, y) is outside of the background area, immediately return */ - if (x < background_area->x || - x > background_area->x + background_area->width || - y < background_area->y || - y > background_area->y + background_area->height) - return NULL; - - /* If (x, y) is inside the background area, clamp it to the cell_area - * so that a cell is still returned. The main reason for doing this - * (on the x axis) is for handling clicks in the indentation area - * (either at the left or right depending on RTL setting). Another - * reason is for handling clicks on the area where the focus rectangle - * is drawn (this is outside of cell area), this manifests itself - * mainly when a large setting is used for focus-line-width. - */ - if (x < cell_area->x) - x = cell_area->x; - else if (x > cell_area->x + cell_area->width) - x = cell_area->x + cell_area->width; - - if (y < cell_area->y) - y = cell_area->y; - else if (y > cell_area->y + cell_area->height) - y = cell_area->y + cell_area->height; - - match = gtk_cell_area_get_cell_at_position (priv->cell_area, - priv->cell_area_context, - priv->tree_view, - cell_area, - x, y, - NULL); - - return match; -} - -gboolean -_gtk_tree_view_column_is_blank_at_pos (GtkTreeViewColumn *column, - GdkRectangle *cell_area, - GdkRectangle *background_area, - int x, - int y) -{ - GtkCellRenderer *match; - GdkRectangle cell_alloc, aligned_area, inner_area; - GtkTreeViewColumnPrivate *priv = column->priv; - - match = _gtk_tree_view_column_get_cell_at_pos (column, - cell_area, - background_area, - x, y); - if (!match) - return FALSE; - - gtk_cell_area_get_cell_allocation (priv->cell_area, - priv->cell_area_context, - priv->tree_view, - match, - cell_area, - &cell_alloc); - - gtk_cell_area_inner_cell_area (priv->cell_area, priv->tree_view, - &cell_alloc, &inner_area); - gtk_cell_renderer_get_aligned_area (match, priv->tree_view, 0, - &inner_area, &aligned_area); - - if (x < aligned_area.x || - x > aligned_area.x + aligned_area.width || - y < aligned_area.y || - y > aligned_area.y + aligned_area.height) - return TRUE; - - return FALSE; -} - -/* Public Functions */ - - -/** - * gtk_tree_view_column_new: - * - * Creates a new `GtkTreeViewColumn`. - * - * Returns: A newly created `GtkTreeViewColumn`. - **/ -GtkTreeViewColumn * -gtk_tree_view_column_new (void) -{ - GtkTreeViewColumn *tree_column; - - tree_column = g_object_new (GTK_TYPE_TREE_VIEW_COLUMN, NULL); - - return tree_column; -} - -/** - * gtk_tree_view_column_new_with_area: - * @area: the `GtkCellArea` that the newly created column should use to layout cells. - * - * Creates a new `GtkTreeViewColumn` using @area to render its cells. - * - * Returns: A newly created `GtkTreeViewColumn`. - */ -GtkTreeViewColumn * -gtk_tree_view_column_new_with_area (GtkCellArea *area) -{ - GtkTreeViewColumn *tree_column; - - tree_column = g_object_new (GTK_TYPE_TREE_VIEW_COLUMN, "cell-area", area, NULL); - - return tree_column; -} - - -/** - * gtk_tree_view_column_new_with_attributes: - * @title: The title to set the header to - * @cell: The `GtkCellRenderer` - * @...: A %NULL-terminated list of attributes - * - * Creates a new `GtkTreeViewColumn` with a number of default values. - * This is equivalent to calling gtk_tree_view_column_set_title(), - * gtk_tree_view_column_pack_start(), and - * gtk_tree_view_column_set_attributes() on the newly created `GtkTreeViewColumn`. - * - * Here’s a simple example: - * |[ - * enum { TEXT_COLUMN, COLOR_COLUMN, N_COLUMNS }; - * // ... - * { - * GtkTreeViewColumn *column; - * GtkCellRenderer *renderer = gtk_cell_renderer_text_new (); - * - * column = gtk_tree_view_column_new_with_attributes ("Title", - * renderer, - * "text", TEXT_COLUMN, - * "foreground", COLOR_COLUMN, - * NULL); - * } - * ]| - * - * Returns: A newly created `GtkTreeViewColumn`. - **/ -GtkTreeViewColumn * -gtk_tree_view_column_new_with_attributes (const char *title, - GtkCellRenderer *cell, - ...) -{ - GtkTreeViewColumn *retval; - va_list args; - - retval = gtk_tree_view_column_new (); - - gtk_tree_view_column_set_title (retval, title); - gtk_tree_view_column_pack_start (retval, cell, TRUE); - - va_start (args, cell); - gtk_tree_view_column_set_attributesv (retval, cell, args); - va_end (args); - - return retval; -} - -/** - * gtk_tree_view_column_pack_start: - * @tree_column: A `GtkTreeViewColumn`. - * @cell: The `GtkCellRenderer` - * @expand: %TRUE if @cell is to be given extra space allocated to @tree_column. - * - * Packs the @cell into the beginning of the column. If @expand is %FALSE, then - * the @cell is allocated no more space than it needs. Any unused space is divided - * evenly between cells for which @expand is %TRUE. - **/ -void -gtk_tree_view_column_pack_start (GtkTreeViewColumn *tree_column, - GtkCellRenderer *cell, - gboolean expand) -{ - gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (tree_column), cell, expand); -} - -/** - * gtk_tree_view_column_pack_end: - * @tree_column: A `GtkTreeViewColumn`. - * @cell: The `GtkCellRenderer` - * @expand: %TRUE if @cell is to be given extra space allocated to @tree_column. - * - * Adds the @cell to end of the column. If @expand is %FALSE, then the @cell - * is allocated no more space than it needs. Any unused space is divided - * evenly between cells for which @expand is %TRUE. - **/ -void -gtk_tree_view_column_pack_end (GtkTreeViewColumn *tree_column, - GtkCellRenderer *cell, - gboolean expand) -{ - gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (tree_column), cell, expand); -} - -/** - * gtk_tree_view_column_clear: - * @tree_column: A `GtkTreeViewColumn` - * - * Unsets all the mappings on all renderers on the @tree_column. - **/ -void -gtk_tree_view_column_clear (GtkTreeViewColumn *tree_column) -{ - gtk_cell_layout_clear (GTK_CELL_LAYOUT (tree_column)); -} - -/** - * gtk_tree_view_column_add_attribute: - * @tree_column: A `GtkTreeViewColumn` - * @cell_renderer: the `GtkCellRenderer` to set attributes on - * @attribute: An attribute on the renderer - * @column: The column position on the model to get the attribute from. - * - * Adds an attribute mapping to the list in @tree_column. - * - * The @column is the - * column of the model to get a value from, and the @attribute is the - * parameter on @cell_renderer to be set from the value. So for example - * if column 2 of the model contains strings, you could have the - * “text” attribute of a `GtkCellRendererText` get its values from - * column 2. - **/ -void -gtk_tree_view_column_add_attribute (GtkTreeViewColumn *tree_column, - GtkCellRenderer *cell_renderer, - const char *attribute, - int column) -{ - gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (tree_column), - cell_renderer, attribute, column); -} - -static void -gtk_tree_view_column_set_attributesv (GtkTreeViewColumn *tree_column, - GtkCellRenderer *cell_renderer, - va_list args) -{ - GtkTreeViewColumnPrivate *priv = tree_column->priv; - char *attribute; - int column; - - attribute = va_arg (args, char *); - - gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (priv->cell_area), - cell_renderer); - - while (attribute != NULL) - { - column = va_arg (args, int); - gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (priv->cell_area), - cell_renderer, attribute, column); - attribute = va_arg (args, char *); - } -} - -/** - * gtk_tree_view_column_set_attributes: - * @tree_column: A `GtkTreeViewColumn` - * @cell_renderer: the `GtkCellRenderer` we’re setting the attributes of - * @...: A %NULL-terminated list of attributes - * - * Sets the attributes in the list as the attributes of @tree_column. - * - * The attributes should be in attribute/column order, as in - * gtk_tree_view_column_add_attribute(). All existing attributes - * are removed, and replaced with the new attributes. - */ -void -gtk_tree_view_column_set_attributes (GtkTreeViewColumn *tree_column, - GtkCellRenderer *cell_renderer, - ...) -{ - va_list args; - - g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); - g_return_if_fail (GTK_IS_CELL_RENDERER (cell_renderer)); - - va_start (args, cell_renderer); - gtk_tree_view_column_set_attributesv (tree_column, cell_renderer, args); - va_end (args); -} - - -/** - * gtk_tree_view_column_set_cell_data_func: - * @tree_column: A `GtkTreeViewColumn` - * @cell_renderer: A `GtkCellRenderer` - * @func: (nullable): The `GtkTreeCellDataFunc` to use. - * @func_data: (closure): The user data for @func. - * @destroy: The destroy notification for @func_data - * - * Sets the `GtkTreeCellDataFunc` to use for the column. - * - * This - * function is used instead of the standard attributes mapping for - * setting the column value, and should set the value of @tree_column's - * cell renderer as appropriate. @func may be %NULL to remove an - * older one. - **/ -void -gtk_tree_view_column_set_cell_data_func (GtkTreeViewColumn *tree_column, - GtkCellRenderer *cell_renderer, - GtkTreeCellDataFunc func, - gpointer func_data, - GDestroyNotify destroy) -{ - gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (tree_column), - cell_renderer, - (GtkCellLayoutDataFunc)func, - func_data, destroy); -} - - -/** - * gtk_tree_view_column_clear_attributes: - * @tree_column: a `GtkTreeViewColumn` - * @cell_renderer: a `GtkCellRenderer` to clear the attribute mapping on. - * - * Clears all existing attributes previously set with - * gtk_tree_view_column_set_attributes(). - **/ -void -gtk_tree_view_column_clear_attributes (GtkTreeViewColumn *tree_column, - GtkCellRenderer *cell_renderer) -{ - gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (tree_column), - cell_renderer); -} - -/** - * gtk_tree_view_column_set_spacing: - * @tree_column: A `GtkTreeViewColumn`. - * @spacing: distance between cell renderers in pixels. - * - * Sets the spacing field of @tree_column, which is the number of pixels to - * place between cell renderers packed into it. - **/ -void -gtk_tree_view_column_set_spacing (GtkTreeViewColumn *tree_column, - int spacing) -{ - GtkTreeViewColumnPrivate *priv; - - g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); - g_return_if_fail (spacing >= 0); - - priv = tree_column->priv; - - if (gtk_cell_area_box_get_spacing (GTK_CELL_AREA_BOX (priv->cell_area)) != spacing) - { - gtk_cell_area_box_set_spacing (GTK_CELL_AREA_BOX (priv->cell_area), spacing); - if (priv->tree_view) - _gtk_tree_view_column_cell_set_dirty (tree_column, TRUE); - g_object_notify_by_pspec (G_OBJECT (tree_column), tree_column_props[PROP_SPACING]); - } -} - -/** - * gtk_tree_view_column_get_spacing: - * @tree_column: A `GtkTreeViewColumn`. - * - * Returns the spacing of @tree_column. - * - * Returns: the spacing of @tree_column. - **/ -int -gtk_tree_view_column_get_spacing (GtkTreeViewColumn *tree_column) -{ - GtkTreeViewColumnPrivate *priv; - - g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), 0); - - priv = tree_column->priv; - - return gtk_cell_area_box_get_spacing (GTK_CELL_AREA_BOX (priv->cell_area)); -} - -/* Options for manipulating the columns */ - -/** - * gtk_tree_view_column_set_visible: - * @tree_column: A `GtkTreeViewColumn`. - * @visible: %TRUE if the @tree_column is visible. - * - * Sets the visibility of @tree_column. - */ -void -gtk_tree_view_column_set_visible (GtkTreeViewColumn *tree_column, - gboolean visible) -{ - GtkTreeViewColumnPrivate *priv; - - g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); - - priv = tree_column->priv; - visible = !! visible; - - if (priv->visible == visible) - return; - - priv->visible = visible; - - gtk_widget_set_visible (priv->button, visible); - - if (priv->visible) - _gtk_tree_view_column_cell_set_dirty (tree_column, TRUE); - - gtk_tree_view_column_update_button (tree_column); - g_object_notify_by_pspec (G_OBJECT (tree_column), tree_column_props[PROP_VISIBLE]); -} - -/** - * gtk_tree_view_column_get_visible: - * @tree_column: A `GtkTreeViewColumn`. - * - * Returns %TRUE if @tree_column is visible. - * - * Returns: whether the column is visible or not. If it is visible, then - * the tree will show the column. - **/ -gboolean -gtk_tree_view_column_get_visible (GtkTreeViewColumn *tree_column) -{ - g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), FALSE); - - return tree_column->priv->visible; -} - -/** - * gtk_tree_view_column_set_resizable: - * @tree_column: A `GtkTreeViewColumn` - * @resizable: %TRUE, if the column can be resized - * - * If @resizable is %TRUE, then the user can explicitly resize the column by - * grabbing the outer edge of the column button. - * - * If resizable is %TRUE and - * sizing mode of the column is %GTK_TREE_VIEW_COLUMN_AUTOSIZE, then the sizing - * mode is changed to %GTK_TREE_VIEW_COLUMN_GROW_ONLY. - **/ -void -gtk_tree_view_column_set_resizable (GtkTreeViewColumn *tree_column, - gboolean resizable) -{ - GtkTreeViewColumnPrivate *priv; - - g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); - - priv = tree_column->priv; - resizable = !! resizable; - - if (priv->resizable == resizable) - return; - - priv->resizable = resizable; - - if (resizable && priv->column_type == GTK_TREE_VIEW_COLUMN_AUTOSIZE) - gtk_tree_view_column_set_sizing (tree_column, GTK_TREE_VIEW_COLUMN_GROW_ONLY); - - gtk_tree_view_column_update_button (tree_column); - - g_object_notify_by_pspec (G_OBJECT (tree_column), tree_column_props[PROP_RESIZABLE]); -} - -/** - * gtk_tree_view_column_get_resizable: - * @tree_column: A `GtkTreeViewColumn` - * - * Returns %TRUE if the @tree_column can be resized by the end user. - * - * Returns: %TRUE, if the @tree_column can be resized. - **/ -gboolean -gtk_tree_view_column_get_resizable (GtkTreeViewColumn *tree_column) -{ - g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), FALSE); - - return tree_column->priv->resizable; -} - - -/** - * gtk_tree_view_column_set_sizing: - * @tree_column: A `GtkTreeViewColumn`. - * @type: The `GtkTreeViewColumn`Sizing. - * - * Sets the growth behavior of @tree_column to @type. - **/ -void -gtk_tree_view_column_set_sizing (GtkTreeViewColumn *tree_column, - GtkTreeViewColumnSizing type) -{ - GtkTreeViewColumnPrivate *priv; - - g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); - - priv = tree_column->priv; - - if (type == priv->column_type) - return; - - if (type == GTK_TREE_VIEW_COLUMN_AUTOSIZE) - gtk_tree_view_column_set_resizable (tree_column, FALSE); - - priv->column_type = type; - - gtk_tree_view_column_update_button (tree_column); - - g_object_notify_by_pspec (G_OBJECT (tree_column), tree_column_props[PROP_SIZING]); -} - -/** - * gtk_tree_view_column_get_sizing: - * @tree_column: A `GtkTreeViewColumn`. - * - * Returns the current type of @tree_column. - * - * Returns: The type of @tree_column. - **/ -GtkTreeViewColumnSizing -gtk_tree_view_column_get_sizing (GtkTreeViewColumn *tree_column) -{ - g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), 0); - - return tree_column->priv->column_type; -} - -/** - * gtk_tree_view_column_get_width: - * @tree_column: A `GtkTreeViewColumn`. - * - * Returns the current size of @tree_column in pixels. - * - * Returns: The current width of @tree_column. - **/ -int -gtk_tree_view_column_get_width (GtkTreeViewColumn *tree_column) -{ - g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), 0); - - return tree_column->priv->width; -} - -/** - * gtk_tree_view_column_get_x_offset: - * @tree_column: A `GtkTreeViewColumn`. - * - * Returns the current X offset of @tree_column in pixels. - * - * Returns: The current X offset of @tree_column. - */ -int -gtk_tree_view_column_get_x_offset (GtkTreeViewColumn *tree_column) -{ - g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), 0); - - return tree_column->priv->x_offset; -} - -int -_gtk_tree_view_column_request_width (GtkTreeViewColumn *tree_column) -{ - GtkTreeViewColumnPrivate *priv; - int real_requested_width; - - priv = tree_column->priv; - - if (priv->fixed_width != -1) - { - real_requested_width = priv->fixed_width; - } - else if (gtk_tree_view_get_headers_visible (GTK_TREE_VIEW (priv->tree_view))) - { - int button_request; - int requested_width; - - gtk_cell_area_context_get_preferred_width (priv->cell_area_context, &requested_width, NULL); - requested_width += priv->padding; - - gtk_widget_measure (priv->button, GTK_ORIENTATION_HORIZONTAL, -1, - &button_request, NULL, NULL, NULL); - real_requested_width = MAX (requested_width, button_request); - } - else - { - int requested_width; - - gtk_cell_area_context_get_preferred_width (priv->cell_area_context, &requested_width, NULL); - requested_width += priv->padding; - - real_requested_width = requested_width; - if (real_requested_width < 0) - real_requested_width = 0; - } - - if (priv->min_width != -1) - real_requested_width = MAX (real_requested_width, priv->min_width); - - if (priv->max_width != -1) - real_requested_width = MIN (real_requested_width, priv->max_width); - - return real_requested_width; -} - -void -_gtk_tree_view_column_allocate (GtkTreeViewColumn *tree_column, - int x_offset, - int width, - int height) -{ - GtkTreeViewColumnPrivate *priv; - GtkAllocation allocation = { 0, 0, 0, 0 }; - - g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); - - priv = tree_column->priv; - - if (priv->width != width) - gtk_widget_queue_draw (priv->tree_view); - - priv->x_offset = x_offset; - priv->width = width; - - gtk_cell_area_context_allocate (priv->cell_area_context, priv->width - priv->padding, -1); - - if (gtk_tree_view_get_headers_visible (GTK_TREE_VIEW (priv->tree_view))) - { - /* TODO: Underallocates the button horizontally, but - * https://bugzilla.gnome.org/show_bug.cgi?id=770388 - */ - allocation.x = x_offset; - allocation.y = 0; - allocation.width = width; - allocation.height = height; - - gtk_widget_size_allocate (priv->button, &allocation, -1); - } - - g_object_notify_by_pspec (G_OBJECT (tree_column), tree_column_props[PROP_X_OFFSET]); - g_object_notify_by_pspec (G_OBJECT (tree_column), tree_column_props[PROP_WIDTH]); -} - -/** - * gtk_tree_view_column_set_fixed_width: - * @tree_column: A `GtkTreeViewColumn`. - * @fixed_width: The new fixed width, in pixels, or -1. - * - * If @fixed_width is not -1, sets the fixed width of @tree_column; otherwise - * unsets it. The effective value of @fixed_width is clamped between the - * minimum and maximum width of the column; however, the value stored in the - * “fixed-width” property is not clamped. If the column sizing is - * %GTK_TREE_VIEW_COLUMN_GROW_ONLY or %GTK_TREE_VIEW_COLUMN_AUTOSIZE, setting - * a fixed width overrides the automatically calculated width. Note that - * @fixed_width is only a hint to GTK; the width actually allocated to the - * column may be greater or less than requested. - * - * Along with “expand”, the “fixed-width” property changes when the column is - * resized by the user. - **/ -void -gtk_tree_view_column_set_fixed_width (GtkTreeViewColumn *tree_column, - int fixed_width) -{ - GtkTreeViewColumnPrivate *priv; - - g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); - g_return_if_fail (fixed_width >= -1); - - priv = tree_column->priv; - - if (priv->fixed_width != fixed_width) - { - priv->fixed_width = fixed_width; - if (priv->visible && - priv->tree_view != NULL && - gtk_widget_get_realized (priv->tree_view)) - gtk_widget_queue_resize (priv->tree_view); - - g_object_notify_by_pspec (G_OBJECT (tree_column), tree_column_props[PROP_FIXED_WIDTH]); - } -} - -/** - * gtk_tree_view_column_get_fixed_width: - * @tree_column: A `GtkTreeViewColumn`. - * - * Gets the fixed width of the column. This may not be the actual displayed - * width of the column; for that, use gtk_tree_view_column_get_width(). - * - * Returns: The fixed width of the column. - **/ -int -gtk_tree_view_column_get_fixed_width (GtkTreeViewColumn *tree_column) -{ - g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), 0); - - return tree_column->priv->fixed_width; -} - -/** - * gtk_tree_view_column_set_min_width: - * @tree_column: A `GtkTreeViewColumn`. - * @min_width: The minimum width of the column in pixels, or -1. - * - * Sets the minimum width of the @tree_column. If @min_width is -1, then the - * minimum width is unset. - **/ -void -gtk_tree_view_column_set_min_width (GtkTreeViewColumn *tree_column, - int min_width) -{ - GtkTreeViewColumnPrivate *priv; - - g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); - g_return_if_fail (min_width >= -1); - - priv = tree_column->priv; - - if (min_width == priv->min_width) - return; - - if (priv->visible && - priv->tree_view != NULL && - gtk_widget_get_realized (priv->tree_view)) - { - if (min_width > priv->width) - gtk_widget_queue_resize (priv->tree_view); - } - - priv->min_width = min_width; - g_object_freeze_notify (G_OBJECT (tree_column)); - if (priv->max_width != -1 && priv->max_width < min_width) - { - priv->max_width = min_width; - g_object_notify_by_pspec (G_OBJECT (tree_column), tree_column_props[PROP_MAX_WIDTH]); - } - g_object_notify_by_pspec (G_OBJECT (tree_column), tree_column_props[PROP_MIN_WIDTH]); - g_object_thaw_notify (G_OBJECT (tree_column)); - - if (priv->column_type == GTK_TREE_VIEW_COLUMN_AUTOSIZE && priv->tree_view) - _gtk_tree_view_column_autosize (GTK_TREE_VIEW (priv->tree_view), - tree_column); -} - -/** - * gtk_tree_view_column_get_min_width: - * @tree_column: A `GtkTreeViewColumn`. - * - * Returns the minimum width in pixels of the @tree_column, or -1 if no minimum - * width is set. - * - * Returns: The minimum width of the @tree_column. - **/ -int -gtk_tree_view_column_get_min_width (GtkTreeViewColumn *tree_column) -{ - g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), -1); - - return tree_column->priv->min_width; -} - -/** - * gtk_tree_view_column_set_max_width: - * @tree_column: A `GtkTreeViewColumn`. - * @max_width: The maximum width of the column in pixels, or -1. - * - * Sets the maximum width of the @tree_column. If @max_width is -1, then the - * maximum width is unset. Note, the column can actually be wider than max - * width if it’s the last column in a view. In this case, the column expands to - * fill any extra space. - **/ -void -gtk_tree_view_column_set_max_width (GtkTreeViewColumn *tree_column, - int max_width) -{ - GtkTreeViewColumnPrivate *priv; - - g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); - g_return_if_fail (max_width >= -1); - - priv = tree_column->priv; - - if (max_width == priv->max_width) - return; - - if (priv->visible && - priv->tree_view != NULL && - gtk_widget_get_realized (priv->tree_view)) - { - if (max_width != -1 && max_width < priv->width) - gtk_widget_queue_resize (priv->tree_view); - } - - priv->max_width = max_width; - g_object_freeze_notify (G_OBJECT (tree_column)); - if (max_width != -1 && max_width < priv->min_width) - { - priv->min_width = max_width; - g_object_notify_by_pspec (G_OBJECT (tree_column), tree_column_props[PROP_MIN_WIDTH]); - } - g_object_notify_by_pspec (G_OBJECT (tree_column), tree_column_props[PROP_MAX_WIDTH]); - g_object_thaw_notify (G_OBJECT (tree_column)); - - if (priv->column_type == GTK_TREE_VIEW_COLUMN_AUTOSIZE && priv->tree_view) - _gtk_tree_view_column_autosize (GTK_TREE_VIEW (priv->tree_view), - tree_column); -} - -/** - * gtk_tree_view_column_get_max_width: - * @tree_column: A `GtkTreeViewColumn`. - * - * Returns the maximum width in pixels of the @tree_column, or -1 if no maximum - * width is set. - * - * Returns: The maximum width of the @tree_column. - **/ -int -gtk_tree_view_column_get_max_width (GtkTreeViewColumn *tree_column) -{ - g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), -1); - - return tree_column->priv->max_width; -} - -/** - * gtk_tree_view_column_clicked: - * @tree_column: a `GtkTreeViewColumn` - * - * Emits the “clicked” signal on the column. This function will only work if - * @tree_column is clickable. - **/ -void -gtk_tree_view_column_clicked (GtkTreeViewColumn *tree_column) -{ - GtkTreeViewColumnPrivate *priv; - - g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); - - priv = tree_column->priv; - - if (priv->visible && priv->clickable) - g_signal_emit_by_name (priv->button, "clicked"); -} - -/** - * gtk_tree_view_column_set_title: - * @tree_column: A `GtkTreeViewColumn`. - * @title: The title of the @tree_column. - * - * Sets the title of the @tree_column. If a custom widget has been set, then - * this value is ignored. - **/ -void -gtk_tree_view_column_set_title (GtkTreeViewColumn *tree_column, - const char *title) -{ - GtkTreeViewColumnPrivate *priv; - char *new_title; - - g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); - - priv = tree_column->priv; - - new_title = g_strdup (title); - g_free (priv->title); - priv->title = new_title; - - gtk_tree_view_column_update_button (tree_column); - g_object_notify_by_pspec (G_OBJECT (tree_column), tree_column_props[PROP_TITLE]); -} - -/** - * gtk_tree_view_column_get_title: - * @tree_column: A `GtkTreeViewColumn`. - * - * Returns the title of the widget. - * - * Returns: the title of the column. This string should not be - * modified or freed. - **/ -const char * -gtk_tree_view_column_get_title (GtkTreeViewColumn *tree_column) -{ - g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), NULL); - - return tree_column->priv->title; -} - -/** - * gtk_tree_view_column_set_expand: - * @tree_column: A `GtkTreeViewColumn`. - * @expand: %TRUE if the column should expand to fill available space. - * - * Sets the column to take available extra space. This space is shared equally - * amongst all columns that have the expand set to %TRUE. If no column has this - * option set, then the last column gets all extra space. By default, every - * column is created with this %FALSE. - * - * Along with “fixed-width”, the “expand” property changes when the column is - * resized by the user. - **/ -void -gtk_tree_view_column_set_expand (GtkTreeViewColumn *tree_column, - gboolean expand) -{ - GtkTreeViewColumnPrivate *priv; - - g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); - - priv = tree_column->priv; - - expand = expand?TRUE:FALSE; - if (priv->expand == expand) - return; - priv->expand = expand; - - if (priv->visible && - priv->tree_view != NULL && - gtk_widget_get_realized (priv->tree_view)) - { - gtk_widget_queue_resize (priv->tree_view); - } - - g_object_notify_by_pspec (G_OBJECT (tree_column), tree_column_props[PROP_EXPAND]); -} - -/** - * gtk_tree_view_column_get_expand: - * @tree_column: A `GtkTreeViewColumn`. - * - * Returns %TRUE if the column expands to fill available space. - * - * Returns: %TRUE if the column expands to fill available space. - **/ -gboolean -gtk_tree_view_column_get_expand (GtkTreeViewColumn *tree_column) -{ - g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), FALSE); - - return tree_column->priv->expand; -} - -/** - * gtk_tree_view_column_set_clickable: - * @tree_column: A `GtkTreeViewColumn`. - * @clickable: %TRUE if the header is active. - * - * Sets the header to be active if @clickable is %TRUE. When the header is - * active, then it can take keyboard focus, and can be clicked. - **/ -void -gtk_tree_view_column_set_clickable (GtkTreeViewColumn *tree_column, - gboolean clickable) -{ - GtkTreeViewColumnPrivate *priv; - - g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); - - priv = tree_column->priv; - - clickable = !! clickable; - if (priv->clickable == clickable) - return; - - priv->clickable = clickable; - gtk_tree_view_column_update_button (tree_column); - g_object_notify_by_pspec (G_OBJECT (tree_column), tree_column_props[PROP_CLICKABLE]); -} - -/** - * gtk_tree_view_column_get_clickable: - * @tree_column: a `GtkTreeViewColumn` - * - * Returns %TRUE if the user can click on the header for the column. - * - * Returns: %TRUE if user can click the column header. - **/ -gboolean -gtk_tree_view_column_get_clickable (GtkTreeViewColumn *tree_column) -{ - g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), FALSE); - - return tree_column->priv->clickable; -} - -/** - * gtk_tree_view_column_set_widget: - * @tree_column: A `GtkTreeViewColumn`. - * @widget: (nullable): A child `GtkWidget` - * - * Sets the widget in the header to be @widget. If widget is %NULL, then the - * header button is set with a `GtkLabel` set to the title of @tree_column. - **/ -void -gtk_tree_view_column_set_widget (GtkTreeViewColumn *tree_column, - GtkWidget *widget) -{ - GtkTreeViewColumnPrivate *priv; - - g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); - g_return_if_fail (widget == NULL || GTK_IS_WIDGET (widget)); - - priv = tree_column->priv; - - if (widget) - g_object_ref_sink (widget); - - if (priv->child) - g_object_unref (priv->child); - - priv->child = widget; - gtk_tree_view_column_update_button (tree_column); - g_object_notify_by_pspec (G_OBJECT (tree_column), tree_column_props[PROP_WIDGET]); -} - -/** - * gtk_tree_view_column_get_widget: - * @tree_column: A `GtkTreeViewColumn` - * - * Returns the `GtkWidget` in the button on the column header. - * - * If a custom widget has not been set then %NULL is returned. - * - * Returns: (nullable) (transfer none): The `GtkWidget` in the column header - */ -GtkWidget * -gtk_tree_view_column_get_widget (GtkTreeViewColumn *tree_column) -{ - g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), NULL); - - return tree_column->priv->child; -} - -/** - * gtk_tree_view_column_set_alignment: - * @tree_column: A `GtkTreeViewColumn`. - * @xalign: The alignment, which is between [0.0 and 1.0] inclusive. - * - * Sets the alignment of the title or custom widget inside the column header. - * The alignment determines its location inside the button -- 0.0 for left, 0.5 - * for center, 1.0 for right. - **/ -void -gtk_tree_view_column_set_alignment (GtkTreeViewColumn *tree_column, - float xalign) -{ - GtkTreeViewColumnPrivate *priv; - - g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); - - priv = tree_column->priv; - - xalign = CLAMP (xalign, 0.0, 1.0); - - if (priv->xalign == xalign) - return; - - priv->xalign = xalign; - gtk_tree_view_column_update_button (tree_column); - g_object_notify_by_pspec (G_OBJECT (tree_column), tree_column_props[PROP_ALIGNMENT]); -} - -/** - * gtk_tree_view_column_get_alignment: - * @tree_column: A `GtkTreeViewColumn`. - * - * Returns the current x alignment of @tree_column. This value can range - * between 0.0 and 1.0. - * - * Returns: The current alignent of @tree_column. - **/ -float -gtk_tree_view_column_get_alignment (GtkTreeViewColumn *tree_column) -{ - g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), 0.5); - - return tree_column->priv->xalign; -} - -/** - * gtk_tree_view_column_set_reorderable: - * @tree_column: A `GtkTreeViewColumn` - * @reorderable: %TRUE, if the column can be reordered. - * - * If @reorderable is %TRUE, then the column can be reordered by the end user - * dragging the header. - **/ -void -gtk_tree_view_column_set_reorderable (GtkTreeViewColumn *tree_column, - gboolean reorderable) -{ - GtkTreeViewColumnPrivate *priv; - - g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); - - priv = tree_column->priv; - - /* if (reorderable) - gtk_tree_view_column_set_clickable (tree_column, TRUE);*/ - - if (priv->reorderable == (reorderable?TRUE:FALSE)) - return; - - priv->reorderable = (reorderable?TRUE:FALSE); - gtk_tree_view_column_update_button (tree_column); - g_object_notify_by_pspec (G_OBJECT (tree_column), tree_column_props[PROP_REORDERABLE]); -} - -/** - * gtk_tree_view_column_get_reorderable: - * @tree_column: A `GtkTreeViewColumn` - * - * Returns %TRUE if the @tree_column can be reordered by the user. - * - * Returns: %TRUE if the @tree_column can be reordered by the user. - **/ -gboolean -gtk_tree_view_column_get_reorderable (GtkTreeViewColumn *tree_column) -{ - g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), FALSE); - - return tree_column->priv->reorderable; -} - - -/** - * gtk_tree_view_column_set_sort_column_id: - * @tree_column: a `GtkTreeViewColumn` - * @sort_column_id: The @sort_column_id of the model to sort on. - * - * Sets the logical @sort_column_id that this column sorts on when this column - * is selected for sorting. Doing so makes the column header clickable. - **/ -void -gtk_tree_view_column_set_sort_column_id (GtkTreeViewColumn *tree_column, - int sort_column_id) -{ - GtkTreeViewColumnPrivate *priv; - - g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); - g_return_if_fail (sort_column_id >= -1); - - priv = tree_column->priv; - - if (priv->sort_column_id == sort_column_id) - return; - - priv->sort_column_id = sort_column_id; - - /* Handle unsetting the id */ - if (sort_column_id == -1) - { - GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree_view)); - - if (priv->sort_clicked_signal) - { - g_signal_handler_disconnect (tree_column, priv->sort_clicked_signal); - priv->sort_clicked_signal = 0; - } - - if (priv->sort_column_changed_signal) - { - g_signal_handler_disconnect (model, priv->sort_column_changed_signal); - priv->sort_column_changed_signal = 0; - } - - gtk_tree_view_column_set_sort_order (tree_column, GTK_SORT_ASCENDING); - gtk_tree_view_column_set_sort_indicator (tree_column, FALSE); - gtk_tree_view_column_set_clickable (tree_column, FALSE); - g_object_notify_by_pspec (G_OBJECT (tree_column), tree_column_props[PROP_SORT_COLUMN_ID]); - return; - } - - gtk_tree_view_column_set_clickable (tree_column, TRUE); - - if (! priv->sort_clicked_signal) - priv->sort_clicked_signal = g_signal_connect (tree_column, - "clicked", - G_CALLBACK (gtk_tree_view_column_sort), - NULL); - - gtk_tree_view_column_setup_sort_column_id_callback (tree_column); - g_object_notify_by_pspec (G_OBJECT (tree_column), tree_column_props[PROP_SORT_COLUMN_ID]); -} - -/** - * gtk_tree_view_column_get_sort_column_id: - * @tree_column: a `GtkTreeViewColumn` - * - * Gets the logical @sort_column_id that the model sorts on - * when this column is selected for sorting. - * - * See [method@Gtk.TreeViewColumn.set_sort_column_id]. - * - * Returns: the current @sort_column_id for this column, or -1 if - * this column can’t be used for sorting - */ -int -gtk_tree_view_column_get_sort_column_id (GtkTreeViewColumn *tree_column) -{ - g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), 0); - - return tree_column->priv->sort_column_id; -} - -/** - * gtk_tree_view_column_set_sort_indicator: - * @tree_column: a `GtkTreeViewColumn` - * @setting: %TRUE to display an indicator that the column is sorted - * - * Call this function with a @setting of %TRUE to display an arrow in - * the header button indicating the column is sorted. Call - * gtk_tree_view_column_set_sort_order() to change the direction of - * the arrow. - * - **/ -void -gtk_tree_view_column_set_sort_indicator (GtkTreeViewColumn *tree_column, - gboolean setting) -{ - g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); - - setting = setting != FALSE; - - if (setting == tree_column->priv->show_sort_indicator) - return; - - tree_column->priv->show_sort_indicator = setting; - gtk_tree_view_column_update_button (tree_column); - g_object_notify_by_pspec (G_OBJECT (tree_column), tree_column_props[PROP_SORT_INDICATOR]); -} - -/** - * gtk_tree_view_column_get_sort_indicator: - * @tree_column: a `GtkTreeViewColumn` - * - * Gets the value set by gtk_tree_view_column_set_sort_indicator(). - * - * Returns: whether the sort indicator arrow is displayed - **/ -gboolean -gtk_tree_view_column_get_sort_indicator (GtkTreeViewColumn *tree_column) -{ - g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), FALSE); - - return tree_column->priv->show_sort_indicator; -} - -/** - * gtk_tree_view_column_set_sort_order: - * @tree_column: a `GtkTreeViewColumn` - * @order: sort order that the sort indicator should indicate - * - * Changes the appearance of the sort indicator. - * - * This does not actually sort the model. Use - * gtk_tree_view_column_set_sort_column_id() if you want automatic sorting - * support. This function is primarily for custom sorting behavior, and should - * be used in conjunction with gtk_tree_sortable_set_sort_column_id() to do - * that. For custom models, the mechanism will vary. - * - * The sort indicator changes direction to indicate normal sort or reverse sort. - * Note that you must have the sort indicator enabled to see anything when - * calling this function; see gtk_tree_view_column_set_sort_indicator(). - **/ -void -gtk_tree_view_column_set_sort_order (GtkTreeViewColumn *tree_column, - GtkSortType order) -{ - g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); - - if (order == tree_column->priv->sort_order) - return; - - tree_column->priv->sort_order = order; - gtk_tree_view_column_update_button (tree_column); - g_object_notify_by_pspec (G_OBJECT (tree_column), tree_column_props[PROP_SORT_ORDER]); -} - -/** - * gtk_tree_view_column_get_sort_order: - * @tree_column: a `GtkTreeViewColumn` - * - * Gets the value set by gtk_tree_view_column_set_sort_order(). - * - * Returns: the sort order the sort indicator is indicating - **/ -GtkSortType -gtk_tree_view_column_get_sort_order (GtkTreeViewColumn *tree_column) -{ - g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), 0); - - return tree_column->priv->sort_order; -} - -/** - * gtk_tree_view_column_cell_set_cell_data: - * @tree_column: A `GtkTreeViewColumn`. - * @tree_model: The `GtkTreeModel` to get the cell renderers attributes from. - * @iter: The `GtkTreeIter` to get the cell renderer’s attributes from. - * @is_expander: %TRUE, if the row has children - * @is_expanded: %TRUE, if the row has visible children - * - * Sets the cell renderer based on the @tree_model and @iter. That is, for - * every attribute mapping in @tree_column, it will get a value from the set - * column on the @iter, and use that value to set the attribute on the cell - * renderer. This is used primarily by the `GtkTreeView`. - **/ -void -gtk_tree_view_column_cell_set_cell_data (GtkTreeViewColumn *tree_column, - GtkTreeModel *tree_model, - GtkTreeIter *iter, - gboolean is_expander, - gboolean is_expanded) -{ - g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); - - if (tree_model == NULL) - return; - - gtk_cell_area_apply_attributes (tree_column->priv->cell_area, tree_model, iter, - is_expander, is_expanded); -} - -/** - * gtk_tree_view_column_cell_get_size: - * @tree_column: A `GtkTreeViewColumn`. - * @x_offset: (out) (optional): location to return x offset of a cell relative to @cell_area - * @y_offset: (out) (optional): location to return y offset of a cell relative to @cell_area - * @width: (out) (optional): location to return width needed to render a cell - * @height: (out) (optional): location to return height needed to render a cell - * - * Obtains the width and height needed to render the column. This is used - * primarily by the `GtkTreeView`. - **/ -void -gtk_tree_view_column_cell_get_size (GtkTreeViewColumn *tree_column, - int *x_offset, - int *y_offset, - int *width, - int *height) -{ - GtkTreeViewColumnPrivate *priv; - int min_width = 0, min_height = 0; - - g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); - - priv = tree_column->priv; - - g_signal_handler_block (priv->cell_area_context, - priv->context_changed_signal); - - gtk_cell_area_get_preferred_width (priv->cell_area, - priv->cell_area_context, - priv->tree_view, - NULL, NULL); - - gtk_cell_area_context_get_preferred_width (priv->cell_area_context, &min_width, NULL); - - gtk_cell_area_get_preferred_height_for_width (priv->cell_area, - priv->cell_area_context, - priv->tree_view, - min_width, - &min_height, - NULL); - - g_signal_handler_unblock (priv->cell_area_context, - priv->context_changed_signal); - - - if (height) - * height = min_height; - if (width) - * width = min_width; - -} - -/** - * gtk_tree_view_column_cell_snapshot: - * @tree_column: A `GtkTreeViewColumn`. - * @snapshot: `GtkSnapshot` to draw to - * @background_area: entire cell area (including tree expanders and maybe padding on the sides) - * @cell_area: area normally rendered by a cell renderer - * @flags: flags that affect rendering - * - * Renders the cell contained by #tree_column. This is used primarily by the - * `GtkTreeView`. - **/ -void -gtk_tree_view_column_cell_snapshot (GtkTreeViewColumn *tree_column, - GtkSnapshot *snapshot, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - guint flags, - gboolean draw_focus) -{ - GtkTreeViewColumnPrivate *priv; - - g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); - g_return_if_fail (snapshot != NULL); - g_return_if_fail (background_area != NULL); - g_return_if_fail (cell_area != NULL); - - priv = tree_column->priv; - - gtk_cell_area_snapshot (priv->cell_area, priv->cell_area_context, - priv->tree_view, snapshot, - background_area, cell_area, flags, - draw_focus); -} - -gboolean -_gtk_tree_view_column_cell_event (GtkTreeViewColumn *tree_column, - GdkEvent *event, - const GdkRectangle *cell_area, - guint flags) -{ - GtkTreeViewColumnPrivate *priv; - - g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), FALSE); - - priv = tree_column->priv; - - return gtk_cell_area_event (priv->cell_area, - priv->cell_area_context, - priv->tree_view, - event, - cell_area, - flags); -} - -/** - * gtk_tree_view_column_cell_is_visible: - * @tree_column: A `GtkTreeViewColumn` - * - * Returns %TRUE if any of the cells packed into the @tree_column are visible. - * For this to be meaningful, you must first initialize the cells with - * gtk_tree_view_column_cell_set_cell_data() - * - * Returns: %TRUE, if any of the cells packed into the @tree_column are currently visible - **/ -gboolean -gtk_tree_view_column_cell_is_visible (GtkTreeViewColumn *tree_column) -{ - GList *list; - GList *cells; - GtkTreeViewColumnPrivate *priv; - - g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), FALSE); - - priv = tree_column->priv; - - cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (priv->cell_area)); - for (list = cells; list; list = list->next) - { - if (gtk_cell_renderer_get_visible (list->data)) - { - g_list_free (cells); - return TRUE; - } - } - - g_list_free (cells); - - return FALSE; -} - -/** - * gtk_tree_view_column_focus_cell: - * @tree_column: A `GtkTreeViewColumn` - * @cell: A `GtkCellRenderer` - * - * Sets the current keyboard focus to be at @cell, if the column contains - * 2 or more editable and activatable cells. - **/ -void -gtk_tree_view_column_focus_cell (GtkTreeViewColumn *tree_column, - GtkCellRenderer *cell) -{ - g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); - g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); - - gtk_cell_area_set_focus_cell (tree_column->priv->cell_area, cell); -} - -void -_gtk_tree_view_column_cell_set_dirty (GtkTreeViewColumn *tree_column, - gboolean install_handler) -{ - GtkTreeViewColumnPrivate *priv = tree_column->priv; - - priv->dirty = TRUE; - priv->padding = 0; - priv->width = 0; - - /* Issue a manual reset on the context to have all - * sizes re-requested for the context. - */ - g_signal_handler_block (priv->cell_area_context, - priv->context_changed_signal); - gtk_cell_area_context_reset (priv->cell_area_context); - g_signal_handler_unblock (priv->cell_area_context, - priv->context_changed_signal); - - if (priv->tree_view && - gtk_widget_get_realized (priv->tree_view)) - { - _gtk_tree_view_install_mark_rows_col_dirty (GTK_TREE_VIEW (priv->tree_view), install_handler); - gtk_widget_queue_resize (priv->tree_view); - } -} - -gboolean -_gtk_tree_view_column_cell_get_dirty (GtkTreeViewColumn *tree_column) -{ - return tree_column->priv->dirty; -} - -/** - * gtk_tree_view_column_cell_get_position: - * @tree_column: a `GtkTreeViewColumn` - * @cell_renderer: a `GtkCellRenderer` - * @x_offset: (out) (optional): return location for the horizontal - * position of @cell within @tree_column - * @width: (out) (optional): return location for the width of @cell - * - * Obtains the horizontal position and size of a cell in a column. - * - * If the cell is not found in the column, @start_pos and @width - * are not changed and %FALSE is returned. - * - * Returns: %TRUE if @cell belongs to @tree_column - */ -gboolean -gtk_tree_view_column_cell_get_position (GtkTreeViewColumn *tree_column, - GtkCellRenderer *cell_renderer, - int *x_offset, - int *width) -{ - GtkTreeViewColumnPrivate *priv; - GdkRectangle cell_area; - GdkRectangle allocation; - - g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), FALSE); - g_return_val_if_fail (GTK_IS_CELL_RENDERER (cell_renderer), FALSE); - - priv = tree_column->priv; - - if (! gtk_cell_area_has_renderer (priv->cell_area, cell_renderer)) - return FALSE; - - gtk_tree_view_get_background_area (GTK_TREE_VIEW (priv->tree_view), - NULL, tree_column, &cell_area); - - gtk_cell_area_get_cell_allocation (priv->cell_area, - priv->cell_area_context, - priv->tree_view, - cell_renderer, - &cell_area, - &allocation); - - if (x_offset) - *x_offset = allocation.x - cell_area.x; - - if (width) - *width = allocation.width; - - return TRUE; -} - -/** - * gtk_tree_view_column_queue_resize: - * @tree_column: A `GtkTreeViewColumn` - * - * Flags the column, and the cell renderers added to this column, to have - * their sizes renegotiated. - **/ -void -gtk_tree_view_column_queue_resize (GtkTreeViewColumn *tree_column) -{ - g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); - - if (tree_column->priv->tree_view) - _gtk_tree_view_column_cell_set_dirty (tree_column, TRUE); -} - -/** - * gtk_tree_view_column_get_tree_view: - * @tree_column: A `GtkTreeViewColumn` - * - * Returns the `GtkTreeView` wherein @tree_column has been inserted. - * If @column is currently not inserted in any tree view, %NULL is - * returned. - * - * Returns: (nullable) (transfer none): The tree view wherein @column - * has been inserted - */ -GtkWidget * -gtk_tree_view_column_get_tree_view (GtkTreeViewColumn *tree_column) -{ - g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), NULL); - - return tree_column->priv->tree_view; -} - -/** - * gtk_tree_view_column_get_button: - * @tree_column: A `GtkTreeViewColumn` - * - * Returns the button used in the treeview column header - * - * Returns: (transfer none): The button for the column header. - */ -GtkWidget * -gtk_tree_view_column_get_button (GtkTreeViewColumn *tree_column) -{ - g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), NULL); - - return tree_column->priv->button; -} - -void -_gtk_tree_view_column_push_padding (GtkTreeViewColumn *column, - int padding) -{ - column->priv->padding = MAX (column->priv->padding, padding); -} - -int -_gtk_tree_view_column_get_requested_width (GtkTreeViewColumn *column) -{ - int requested_width; - - gtk_cell_area_context_get_preferred_width (column->priv->cell_area_context, &requested_width, NULL); - - return requested_width + column->priv->padding; -} - -int -_gtk_tree_view_column_get_drag_x (GtkTreeViewColumn *column) -{ - return column->priv->drag_x; -} - -GtkCellAreaContext * -_gtk_tree_view_column_get_context (GtkTreeViewColumn *column) -{ - return column->priv->cell_area_context; -} - -gboolean -_gtk_tree_view_column_coords_in_resize_rect (GtkTreeViewColumn *column, - double x, - double y) -{ - GtkTreeViewColumnPrivate *priv = column->priv; - graphene_rect_t button_bounds; - - /* x and y are in treeview coordinates. */ - - if (!gtk_widget_get_realized (priv->button) || - !priv->resizable || - !priv->visible) - return FALSE; - - if (!gtk_widget_compute_bounds (priv->button, priv->tree_view, &button_bounds)) - return FALSE; - - if (gtk_widget_get_direction (priv->tree_view) == GTK_TEXT_DIR_LTR) - button_bounds.origin.x += button_bounds.size.width - TREE_VIEW_DRAG_WIDTH; - - button_bounds.size.width = TREE_VIEW_DRAG_WIDTH; - - return graphene_rect_contains_point (&button_bounds, - &(graphene_point_t){x, y}); -} diff --git a/gtk/gtktreeviewcolumn.h b/gtk/gtktreeviewcolumn.h deleted file mode 100644 index 9f7002496a..0000000000 --- a/gtk/gtktreeviewcolumn.h +++ /dev/null @@ -1,254 +0,0 @@ -/* gtktreeviewcolumn.h - * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -#ifndef __GTK_TREE_VIEW_COLUMN_H__ -#define __GTK_TREE_VIEW_COLUMN_H__ - -#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) -#error "Only can be included directly." -#endif - -#include -#include -#include -#include - - -G_BEGIN_DECLS - - -#define GTK_TYPE_TREE_VIEW_COLUMN (gtk_tree_view_column_get_type ()) -#define GTK_TREE_VIEW_COLUMN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_TREE_VIEW_COLUMN, GtkTreeViewColumn)) -#define GTK_IS_TREE_VIEW_COLUMN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_TREE_VIEW_COLUMN)) - -typedef struct _GtkTreeViewColumn GtkTreeViewColumn; - -/** - * GtkTreeViewColumnSizing: - * @GTK_TREE_VIEW_COLUMN_GROW_ONLY: Columns only get bigger in reaction to changes in the model - * @GTK_TREE_VIEW_COLUMN_AUTOSIZE: Columns resize to be the optimal size every time the model changes. - * @GTK_TREE_VIEW_COLUMN_FIXED: Columns are a fixed numbers of pixels wide. - * - * The sizing method the column uses to determine its width. Please note - * that %GTK_TREE_VIEW_COLUMN_AUTOSIZE are inefficient for large views, and - * can make columns appear choppy. - */ -typedef enum -{ - GTK_TREE_VIEW_COLUMN_GROW_ONLY, - GTK_TREE_VIEW_COLUMN_AUTOSIZE, - GTK_TREE_VIEW_COLUMN_FIXED -} GtkTreeViewColumnSizing; - -/** - * GtkTreeCellDataFunc: - * @tree_column: A `GtkTreeViewColumn` - * @cell: The `GtkCellRenderer` that is being rendered by @tree_column - * @tree_model: The `GtkTreeModel` being rendered - * @iter: A `GtkTreeIter` of the current row rendered - * @data: (closure): user data - * - * A function to set the properties of a cell instead of just using the - * straight mapping between the cell and the model. - * - * This function is useful for customizing the cell renderer. For example, - * a function might get an* integer from the @tree_model, and render it to - * the “text” attribute of “cell” by converting it to its written equivalent. - * - * See also: gtk_tree_view_column_set_cell_data_func() - */ -typedef void (* GtkTreeCellDataFunc) (GtkTreeViewColumn *tree_column, - GtkCellRenderer *cell, - GtkTreeModel *tree_model, - GtkTreeIter *iter, - gpointer data); - - -GDK_AVAILABLE_IN_ALL -GType gtk_tree_view_column_get_type (void) G_GNUC_CONST; -GDK_AVAILABLE_IN_ALL -GtkTreeViewColumn *gtk_tree_view_column_new (void); -GDK_AVAILABLE_IN_ALL -GtkTreeViewColumn *gtk_tree_view_column_new_with_area (GtkCellArea *area); -GDK_AVAILABLE_IN_ALL -GtkTreeViewColumn *gtk_tree_view_column_new_with_attributes (const char *title, - GtkCellRenderer *cell, - ...) G_GNUC_NULL_TERMINATED; -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_column_pack_start (GtkTreeViewColumn *tree_column, - GtkCellRenderer *cell, - gboolean expand); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_column_pack_end (GtkTreeViewColumn *tree_column, - GtkCellRenderer *cell, - gboolean expand); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_column_clear (GtkTreeViewColumn *tree_column); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_column_add_attribute (GtkTreeViewColumn *tree_column, - GtkCellRenderer *cell_renderer, - const char *attribute, - int column); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_column_set_attributes (GtkTreeViewColumn *tree_column, - GtkCellRenderer *cell_renderer, - ...) G_GNUC_NULL_TERMINATED; -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_column_set_cell_data_func (GtkTreeViewColumn *tree_column, - GtkCellRenderer *cell_renderer, - GtkTreeCellDataFunc func, - gpointer func_data, - GDestroyNotify destroy); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_column_clear_attributes (GtkTreeViewColumn *tree_column, - GtkCellRenderer *cell_renderer); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_column_set_spacing (GtkTreeViewColumn *tree_column, - int spacing); -GDK_AVAILABLE_IN_ALL -int gtk_tree_view_column_get_spacing (GtkTreeViewColumn *tree_column); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_column_set_visible (GtkTreeViewColumn *tree_column, - gboolean visible); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_view_column_get_visible (GtkTreeViewColumn *tree_column); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_column_set_resizable (GtkTreeViewColumn *tree_column, - gboolean resizable); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_view_column_get_resizable (GtkTreeViewColumn *tree_column); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_column_set_sizing (GtkTreeViewColumn *tree_column, - GtkTreeViewColumnSizing type); -GDK_AVAILABLE_IN_ALL -GtkTreeViewColumnSizing gtk_tree_view_column_get_sizing (GtkTreeViewColumn *tree_column); -GDK_AVAILABLE_IN_ALL -int gtk_tree_view_column_get_x_offset (GtkTreeViewColumn *tree_column); -GDK_AVAILABLE_IN_ALL -int gtk_tree_view_column_get_width (GtkTreeViewColumn *tree_column); -GDK_AVAILABLE_IN_ALL -int gtk_tree_view_column_get_fixed_width (GtkTreeViewColumn *tree_column); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_column_set_fixed_width (GtkTreeViewColumn *tree_column, - int fixed_width); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_column_set_min_width (GtkTreeViewColumn *tree_column, - int min_width); -GDK_AVAILABLE_IN_ALL -int gtk_tree_view_column_get_min_width (GtkTreeViewColumn *tree_column); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_column_set_max_width (GtkTreeViewColumn *tree_column, - int max_width); -GDK_AVAILABLE_IN_ALL -int gtk_tree_view_column_get_max_width (GtkTreeViewColumn *tree_column); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_column_clicked (GtkTreeViewColumn *tree_column); - - - -/* Options for manipulating the column headers - */ -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_column_set_title (GtkTreeViewColumn *tree_column, - const char *title); -GDK_AVAILABLE_IN_ALL -const char * gtk_tree_view_column_get_title (GtkTreeViewColumn *tree_column); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_column_set_expand (GtkTreeViewColumn *tree_column, - gboolean expand); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_view_column_get_expand (GtkTreeViewColumn *tree_column); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_column_set_clickable (GtkTreeViewColumn *tree_column, - gboolean clickable); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_view_column_get_clickable (GtkTreeViewColumn *tree_column); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_column_set_widget (GtkTreeViewColumn *tree_column, - GtkWidget *widget); -GDK_AVAILABLE_IN_ALL -GtkWidget *gtk_tree_view_column_get_widget (GtkTreeViewColumn *tree_column); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_column_set_alignment (GtkTreeViewColumn *tree_column, - float xalign); -GDK_AVAILABLE_IN_ALL -float gtk_tree_view_column_get_alignment (GtkTreeViewColumn *tree_column); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_column_set_reorderable (GtkTreeViewColumn *tree_column, - gboolean reorderable); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_view_column_get_reorderable (GtkTreeViewColumn *tree_column); - - - -/* You probably only want to use gtk_tree_view_column_set_sort_column_id. The - * other sorting functions exist primarily to let others do their own custom sorting. - */ -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_column_set_sort_column_id (GtkTreeViewColumn *tree_column, - int sort_column_id); -GDK_AVAILABLE_IN_ALL -int gtk_tree_view_column_get_sort_column_id (GtkTreeViewColumn *tree_column); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_column_set_sort_indicator (GtkTreeViewColumn *tree_column, - gboolean setting); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_view_column_get_sort_indicator (GtkTreeViewColumn *tree_column); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_column_set_sort_order (GtkTreeViewColumn *tree_column, - GtkSortType order); -GDK_AVAILABLE_IN_ALL -GtkSortType gtk_tree_view_column_get_sort_order (GtkTreeViewColumn *tree_column); - - -/* These functions are meant primarily for interaction between the GtkTreeView and the column. - */ -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_column_cell_set_cell_data (GtkTreeViewColumn *tree_column, - GtkTreeModel *tree_model, - GtkTreeIter *iter, - gboolean is_expander, - gboolean is_expanded); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_column_cell_get_size (GtkTreeViewColumn *tree_column, - int *x_offset, - int *y_offset, - int *width, - int *height); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_view_column_cell_is_visible (GtkTreeViewColumn *tree_column); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_column_focus_cell (GtkTreeViewColumn *tree_column, - GtkCellRenderer *cell); -GDK_AVAILABLE_IN_ALL -gboolean gtk_tree_view_column_cell_get_position (GtkTreeViewColumn *tree_column, - GtkCellRenderer *cell_renderer, - int *x_offset, - int *width); -GDK_AVAILABLE_IN_ALL -void gtk_tree_view_column_queue_resize (GtkTreeViewColumn *tree_column); -GDK_AVAILABLE_IN_ALL -GtkWidget *gtk_tree_view_column_get_tree_view (GtkTreeViewColumn *tree_column); -GDK_AVAILABLE_IN_ALL -GtkWidget *gtk_tree_view_column_get_button (GtkTreeViewColumn *tree_column); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkTreeViewColumn, g_object_unref) - -G_END_DECLS - - -#endif /* __GTK_TREE_VIEW_COLUMN_H__ */ diff --git a/gtk/inspector/actions.c b/gtk/inspector/actions.c index d857add4c8..eeac0a188e 100644 --- a/gtk/inspector/actions.c +++ b/gtk/inspector/actions.c @@ -24,7 +24,6 @@ #include "gtkapplication.h" #include "gtkapplicationwindow.h" -#include "gtkliststore.h" #include "gtkwidgetprivate.h" #include "gtkactionmuxerprivate.h" #include "gtkpopover.h" diff --git a/gtk/inspector/meson.build b/gtk/inspector/meson.build index 0ab1b1c47f..813028a26a 100644 --- a/gtk/inspector/meson.build +++ b/gtk/inspector/meson.build @@ -42,7 +42,6 @@ inspector_sources = files( 'statistics.c', 'strv-editor.c', 'tree-data.c', - 'treewalk.c', 'type-info.c', 'updatesoverlay.c', 'variant-editor.c', diff --git a/gtk/inspector/object-tree.c b/gtk/inspector/object-tree.c index 4986ee5518..88e97a9c2c 100644 --- a/gtk/inspector/object-tree.c +++ b/gtk/inspector/object-tree.c @@ -33,14 +33,14 @@ #include "gtkbuildable.h" #include "gtkbutton.h" -#include "gtkcelllayout.h" +#include "deprecated/gtkcelllayout.h" #include "gtkcolumnview.h" -#include "gtkcomboboxprivate.h" +#include "deprecated/gtkcomboboxprivate.h" #include "gtkfilterlistmodel.h" #include "gtkcustomfilter.h" #include "gtkflattenlistmodel.h" #include "gtkbuiltiniconprivate.h" -#include "gtkiconview.h" +#include "deprecated/gtkiconview.h" #include "gtkinscription.h" #include "gtklabel.h" #include "gtklistitem.h" @@ -52,15 +52,16 @@ #include "gtktogglebutton.h" #include "gtktreeexpander.h" #include "gtktreelistmodel.h" -#include "gtktreeview.h" -#include "gtktreeselection.h" -#include "gtktreemodelsort.h" -#include "gtktreemodelfilter.h" +#include "deprecated/gtktreeview.h" +#include "deprecated/gtktreeselection.h" +#include "deprecated/gtktreemodelsort.h" +#include "deprecated/gtktreemodelfilter.h" #include "gtkwidgetprivate.h" #include "gtksearchbar.h" #include "gtksearchentry.h" #include "gtkeventcontrollerkey.h" +G_GNUC_BEGIN_IGNORE_DEPRECATIONS enum { diff --git a/gtk/inspector/object-tree.h b/gtk/inspector/object-tree.h index fe9e0cc7bd..5fc457390e 100644 --- a/gtk/inspector/object-tree.h +++ b/gtk/inspector/object-tree.h @@ -24,7 +24,7 @@ #define _GTK_INSPECTOR_OBJECT_TREE_H_ #include -#include +#include #define GTK_TYPE_INSPECTOR_OBJECT_TREE (gtk_inspector_object_tree_get_type()) #define GTK_INSPECTOR_OBJECT_TREE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_INSPECTOR_OBJECT_TREE, GtkInspectorObjectTree)) diff --git a/gtk/inspector/prop-editor.c b/gtk/inspector/prop-editor.c index cc3dc89e24..94aa7d6d5f 100644 --- a/gtk/inspector/prop-editor.c +++ b/gtk/inspector/prop-editor.c @@ -26,14 +26,14 @@ #include "gtkactionable.h" #include "gtkadjustment.h" #include "gtkapplicationwindow.h" -#include "gtkcelllayout.h" -#include "gtkcellrenderertext.h" +#include "deprecated/gtkcelllayout.h" +#include "deprecated/gtkcombobox.h" +#include "deprecated/gtkiconview.h" +#include "deprecated/gtktreeview.h" #include "gtkcolorbutton.h" #include "gtkcolorchooser.h" -#include "gtkcombobox.h" #include "gtkfontbutton.h" #include "gtkfontchooser.h" -#include "gtkiconview.h" #include "gtklabel.h" #include "gtkpopover.h" #include "gtkscrolledwindow.h" @@ -46,6 +46,8 @@ #include "gtklistbox.h" #include "gtkmenubutton.h" +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + struct _GtkInspectorPropEditor { GtkBox parent_instance; diff --git a/gtk/inspector/recorder.c b/gtk/inspector/recorder.c index 2ef40fe32f..d097f602b6 100644 --- a/gtk/inspector/recorder.c +++ b/gtk/inspector/recorder.c @@ -38,7 +38,6 @@ #include #include #include -#include #include #include #include diff --git a/gtk/inspector/tree-data.c b/gtk/inspector/tree-data.c index 42cec69a84..7873f50b0f 100644 --- a/gtk/inspector/tree-data.c +++ b/gtk/inspector/tree-data.c @@ -22,14 +22,15 @@ #include "object-tree.h" -#include "gtktreeview.h" -#include "gtkcellrenderertext.h" +#include "deprecated/gtktreeview.h" +#include "deprecated/gtkcellrenderertext.h" #include "gtktogglebutton.h" #include "gtklabel.h" #include "gtkstack.h" #include "gtkboxlayout.h" #include "gtkorientable.h" +G_GNUC_BEGIN_IGNORE_DEPRECATIONS struct _GtkInspectorTreeData { diff --git a/gtk/inspector/window.c b/gtk/inspector/window.c index 9603a573d3..1364f34d3f 100644 --- a/gtk/inspector/window.c +++ b/gtk/inspector/window.c @@ -59,7 +59,6 @@ #include "gtkprivate.h" #include "gtknative.h" #include "gtkstack.h" -#include "gtktreeviewcolumn.h" #include "gtkwindowgroup.h" #include "gtkrevealer.h" #include "gtklayoutmanager.h" diff --git a/gtk/meson.build b/gtk/meson.build index 69c3eb1228..b479cf6f4c 100644 --- a/gtk/meson.build +++ b/gtk/meson.build @@ -31,7 +31,6 @@ gtk_private_sources = files([ 'gtkbuilder-menus.c', 'gtkbuilderprecompile.c', 'gtkbuiltinicon.c', - 'gtkcellareaboxcontext.c', 'gtkcoloreditor.c', 'gtkcolorplane.c', 'gtkcolorpicker.c', @@ -148,7 +147,6 @@ gtk_private_sources = files([ 'gtktextviewchild.c', 'timsort/gtktimsort.c', 'gtktrashmonitor.c', - 'gtktreedatalist.c', ]) # List of files that contain public API, and should be introspected @@ -179,21 +177,6 @@ gtk_public_sources = files([ 'gtkbuilderscope.c', 'gtkbutton.c', 'gtkcalendar.c', - 'gtkcellarea.c', - 'gtkcellareabox.c', - 'gtkcellareacontext.c', - 'gtkcelleditable.c', - 'gtkcelllayout.c', - 'gtkcellrenderer.c', - 'gtkcellrendereraccel.c', - 'gtkcellrenderercombo.c', - 'gtkcellrendererpixbuf.c', - 'gtkcellrendererprogress.c', - 'gtkcellrendererspin.c', - 'gtkcellrendererspinner.c', - 'gtkcellrenderertext.c', - 'gtkcellrenderertoggle.c', - 'gtkcellview.c', 'gtkcenterbox.c', 'gtkcenterlayout.c', 'gtkcheckbutton.c', @@ -205,8 +188,6 @@ gtk_public_sources = files([ 'gtkcolumnview.c', 'gtkcolumnviewcolumn.c', 'gtkcolumnviewsorter.c', - 'gtkcombobox.c', - 'gtkcomboboxtext.c', 'gtkcomposetable.c', 'gtkconstraintguide.c', 'gtkconstraintlayout.c', @@ -271,7 +252,6 @@ gtk_public_sources = files([ 'gtkgridview.c', 'gtkheaderbar.c', 'gtkicontheme.c', - 'gtkiconview.c', 'gtkimage.c', 'gtkimagedefinition.c', 'gtkimcontext.c', @@ -292,7 +272,6 @@ gtk_public_sources = files([ 'gtklistitemmanager.c', 'gtklistitemwidget.c', 'gtklistlistmodel.c', - 'gtkliststore.c', 'gtklistview.c', 'gtklockbutton.c', 'gtkmain.c', @@ -333,7 +312,6 @@ gtk_public_sources = files([ 'gtkprogressbar.c', 'gtkpropertylookuplistmodel.c', 'gtkrange.c', - 'gtktreerbtree.c', 'gtkrecentmanager.c', 'gtkrender.c', 'gtkrenderbackground.c', @@ -408,19 +386,9 @@ gtk_public_sources = files([ 'gtktogglebutton.c', 'gtktooltip.c', 'gtktooltipwindow.c', - 'gtktreednd.c', 'gtktreeexpander.c', 'gtktreelistmodel.c', 'gtktreelistrowsorter.c', - 'gtktreemodel.c', - 'gtktreemodelfilter.c', - 'gtktreemodelsort.c', - 'gtktreepopover.c', - 'gtktreeselection.c', - 'gtktreesortable.c', - 'gtktreestore.c', - 'gtktreeview.c', - 'gtktreeviewcolumn.c', 'gtkversion.c', 'gtkvideo.c', 'gtkviewport.c', @@ -468,21 +436,6 @@ gtk_public_headers = files([ 'gtkcalendar.h', 'gtkcenterbox.h', 'gtkcenterlayout.h', - 'gtkcellarea.h', - 'gtkcellareabox.h', - 'gtkcellareacontext.h', - 'gtkcelleditable.h', - 'gtkcelllayout.h', - 'gtkcellrenderer.h', - 'gtkcellrendereraccel.h', - 'gtkcellrenderercombo.h', - 'gtkcellrendererpixbuf.h', - 'gtkcellrendererprogress.h', - 'gtkcellrendererspin.h', - 'gtkcellrendererspinner.h', - 'gtkcellrenderertext.h', - 'gtkcellrenderertoggle.h', - 'gtkcellview.h', 'gtkcheckbutton.h', 'gtkcolorbutton.h', 'gtkcolorchooser.h', @@ -491,8 +444,6 @@ gtk_public_headers = files([ 'gtkcolorutils.h', 'gtkcolumnview.h', 'gtkcolumnviewcolumn.h', - 'gtkcombobox.h', - 'gtkcomboboxtext.h', 'gtkconstraintguide.h', 'gtkconstraintlayout.h', 'gtkconstraint.h', @@ -556,7 +507,6 @@ gtk_public_headers = files([ 'gtkgridview.h', 'gtkheaderbar.h', 'gtkicontheme.h', - 'gtkiconview.h', 'gtkimage.h', 'gtkimcontext.h', 'gtkimcontextsimple.h', @@ -573,7 +523,6 @@ gtk_public_headers = files([ 'gtklistbox.h', 'gtklistitem.h', 'gtklistitemfactory.h', - 'gtkliststore.h', 'gtklistview.h', 'gtklockbutton.h', 'gtkmain.h', @@ -670,18 +619,9 @@ gtk_public_headers = files([ 'gtktextview.h', 'gtktogglebutton.h', 'gtktooltip.h', - 'gtktreednd.h', 'gtktreeexpander.h', 'gtktreelistmodel.h', 'gtktreelistrowsorter.h', - 'gtktreemodel.h', - 'gtktreemodelfilter.h', - 'gtktreemodelsort.h', - 'gtktreeselection.h', - 'gtktreesortable.h', - 'gtktreestore.h', - 'gtktreeview.h', - 'gtktreeviewcolumn.h', 'gtktypes.h', 'gtkvideo.h', 'gtkviewport.h', diff --git a/testsuite/gtk/rbtree.c b/testsuite/gtk/rbtree.c index b735b7da47..170ff2bee5 100644 --- a/testsuite/gtk/rbtree.c +++ b/testsuite/gtk/rbtree.c @@ -19,7 +19,7 @@ #include -#include "../../gtk/gtktreerbtreeprivate.h" +#include "../../gtk/deprecated/gtktreerbtreeprivate.h" /* gtk_tree_rbtree_test */